I am using Aspose with Word for a Mail Merge.
I have a 3x2 table as follows
<a> <b>
<c> <d>
<e> <f>
However under certain conditions one field might be blank, if this is the case I'd like to ommit the entire cell - i.e. not just have an empty cell. i.e.
<a> <b>
<e> <d>
<f>
so in the above example c is empty and thus not displayed?
Can this be done?
I have tried IF and blank MERGEFIELDS also NextIf.
#Jon,
You may need to implement the IFieldMergingCallback Interface. The event handler 'FieldMerging' can be used to implement custom control over the mail merge process. For example, you could check the incoming MergeField's value for a 'null' or 'empty string' and then based on this decide to delete the parent Cell. Here is the sample code:
Document doc = new Document(#"C:\Temp\template.docx");
doc.MailMerge.FieldMergingCallback = new HandleMergeFields();
doc.MailMerge.Execute(new string[] { "a", "b", "c", "d", "e", "f" },
new object[] { "<a>", "<b>", "", "<d>", "<e>", "<f>" });
doc.Save(#"C:\Temp\out.doc");
private class HandleMergeFields : IFieldMergingCallback
{
void IFieldMergingCallback.FieldMerging(FieldMergingArgs args)
{
DocumentBuilder builder = new DocumentBuilder(args.Document);
if (string.IsNullOrEmpty(args.FieldValue.ToString()))
{
Field field = args.Field;
Cell cell = field.Start.GetAncestor(NodeType.Cell) as Cell;
// Remove the MergeField
builder.MoveToMergeField(args.FieldName);
//Remove the Cell
cell.Remove();
}
}
void IFieldMergingCallback.ImageFieldMerging(ImageFieldMergingArgs e)
{
// Do nothing
}
}
Related
I am attempting to write either an array function or a dynamic array to Excel via a UDF written in Excel DNA (v.0.34). My result is always a single value instead of the array. What am I doing wrong?
[ExcelFunction(Name = "WriteTestArray")]
public static object[,] WriteTestArray()
{
try
{
return new object[2, 2] { { "one", "two" }, { "three", "four" } };
}
catch
{
return new object[,] { { ExcelError.ExcelErrorValue } };
}
}
For array functions to work with Excel (before the 'dynamic array' support that comes one day in a future version) you need to select the target range, then type in your formula and press Ctrl+Shift+Enter to commit it as an array formula. It will be indicated by curly brackets when displayed - e.g. {=MyFunc(...)}
I need to create a PDF/UA compliant document in iText7. The most important requirement is tagging of all content. When tagging is enabled (by calling PdfDocument.SetTagged() method) most elements added to the document get correct tags.
The issue is with tagging of table header cells. According to ISO 32000-1:2008, table header cells must be tagged as TH and table data cells must be tagged as TD (14.8.4.2.4. Table elements, Table 337).
iText allows to distinguish between header cells and regular cells by using Table.AddHeaderCell() and Table.AddCell() methods. This mechanism properly creates THead and TBody tags for the groups of rows. Unfortunately, the cells themselves are always marked as TD.
Here is sample code for generating a table:
//var pdfDoc = new PdfDocument(...)
pdfDoc.SetTagged();
var doc = new Document(pdfDoc);
var table = new Table(2);
table.AddHeaderCell("Header 0");
table.AddHeaderCell("Header 1");
table.AddCell("Data 0");
table.AddCell("Data 1");
doc.Add(table);
doc.Close();
Here is an example of tagging structure we are getting:
<Table>
<THead>
<TR>
<TD> //must be TH!
<P>
"Header 0"
<TD>
<P>
"Header 1"
<TBody>
<TR>
<TD> //TD is correct here
<P>
"Data 0"
<TD>
<P>
"Data 1"
Is it possible to have iText generating TH tags when AddHeaderCell() method is used?
I am using iText 7.0.0 for .NET (Community edition)
EDIT: Initial answer was in mistakingly given in the context of pdfHTML and not iText7 proper.
The TH tags getting tagged as TD is a side-effect of the current implementation that treats a TH in the same way as a TD.
For iText7
Set the role of the header-cells to TH before adding them to the table:
cell.setRole(PdfName.TH);
For pdfHTML
While it's possible to access the elements after conversion and before adding them to the document, you'll need to traverse the tree of iText element to find and identify tables and their header -cells. It's easier to to overwrite the conversion behavior of tags with a CustomTagWorker. The following code is taken from the accessibility example. For a primer on custom tagworkers, have a look at the configuration blog-post.
Start by creating a custom tagworker that inherits from a TdTagWorker, but overwrites the role right before returning the element-result:
public class TableHeaderTagWorker extends TdTagWorker {
public TableHeaderTagWorker(IElementNode element, ProcessorContext context) {
super(element, context);
}
#Override
public IPropertyContainer getElementResult() {
Cell cell =(Cell) super.getElementResult();
cell.setRole(PdfName.TH);
return super.getElementResult();
}
}
Create a CustomTagWorkerFactory that maps this TagWorker to the TH-tag
public class AccessibilityTagWorkerFactory extends DefaultTagWorkerFactory {
#Override
public ITagWorker getCustomTagWorker(IElementNode tag, ProcessorContext context) {
//This can probably replaced with a regex or string pattern
if(tag.name().equals("h1")){
return new HeaderTagWorker(tag, context,1);
}
if(tag.name().equals("h2")){
return new HeaderTagWorker(tag, context,2);
}
if(tag.name().equals("h3")){
return new HeaderTagWorker(tag, context,3);
}
if(tag.name().equals("h4")){
return new HeaderTagWorker(tag, context,4);
}
if(tag.name().equals("h5")){
return new HeaderTagWorker(tag, context,5);
}
if(tag.name().equals("h6")){
return new HeaderTagWorker(tag, context,6);
}
if(tag.name().equals("th")){
return new TableHeaderTagWorker(tag,context);
}
return null;
}
}
And set the ConvertorProperties to use this custom factory:
ConverterProperties props = new ConverterProperties();
DefaultTagWorkerFactory tagWorkerFactory = new AccessibilityTagWorkerFactory();
props.setTagWorkerFactory(tagWorkerFactory);
HtmlConverter.convertToPdf(new FileInputStream(src), pdfDoc, props);
pdfDoc.close();
Please note that this has changed with iText 7.1. You can no longer call the setRole() function directly, you have to go through the Accessibility Properties. Furthermore, the setRole() function in the Accessibility Properties only accepts a string. So now, it would be:
cell.getAccessibilityProperties().setRole(PdfName.TH.toString());
As seen as Google Api, i can easily put my data into a spreadsheet as below :
namespace MySpreadsheetIntegration
{
class Program
{
static void Main(string[] args)
{
SpreadsheetsService service = new SpreadsheetsService("MySpreadsheetIntegration-v1");
// TODO: Authorize the service object for a specific user (see other sections)
// Instantiate a SpreadsheetQuery object to retrieve spreadsheets.
SpreadsheetQuery query = new SpreadsheetQuery();
// Make a request to the API and get all spreadsheets.
SpreadsheetFeed feed = service.Query(query);
if (feed.Entries.Count == 0)
{
// TODO: There were no spreadsheets, act accordingly.
}
// TODO: Choose a spreadsheet more intelligently based on your
// app's needs.
SpreadsheetEntry spreadsheet = (SpreadsheetEntry)feed.Entries[0];
Console.WriteLine(spreadsheet.Title.Text);
// Get the first worksheet of the first spreadsheet.
// TODO: Choose a worksheet more intelligently based on your
// app's needs.
WorksheetFeed wsFeed = spreadsheet.Worksheets;
WorksheetEntry worksheet = (WorksheetEntry)wsFeed.Entries[0];
// Define the URL to request the list feed of the worksheet.
AtomLink listFeedLink = worksheet.Links.FindService(GDataSpreadsheetsNameTable.ListRel, null);
// Fetch the list feed of the worksheet.
ListQuery listQuery = new ListQuery(listFeedLink.HRef.ToString());
ListFeed listFeed = service.Query(listQuery);
// Create a local representation of the new row.
ListEntry row = new ListEntry();
row.Elements.Add(new ListEntry.Custom() { LocalName = "firstname", Value = "Joe" });
row.Elements.Add(new ListEntry.Custom() { LocalName = "lastname", Value = "Smith" });
row.Elements.Add(new ListEntry.Custom() { LocalName = "age", Value = "26" });
row.Elements.Add(new ListEntry.Custom() { LocalName = "height", Value = "176" });
// Send the new row to the API for insertion.
service.Insert(listFeed, row);
}
}
}
If i wrote "firstname" into the A1 and "lastname" into the B1, this is working, but i want to start this function ie. F21.
I mean, my localname firstname is in the cell F21 and i want google api to put my data "JOE" into F22 cell and ...
How can i do that ?
Regards.
CellFeed will do that, but list feed is more like an SQL style database table.
Suggest you use CellFeed or update your data SQL style, in whole rows.
I gave up with list feed when I discovered how little control you have over the position of the data.
Good examples:
CellFeed
https://gdata-java-client.googlecode.com/svn-history/r51/trunk/java/sample/spreadsheet/cell/CellDemo.java
ListFeed
https://gdata-java-client.googlecode.com/svn-history/r51/trunk/java/sample/spreadsheet/list/ListDemo.java
I am trying to find a way to pass in an optional string list to a query. What I am trying to do is filter a list of tags by the relationship between them. For example if c# was selected my program would suggest only tags that appear in documents with a c# tag and then on the selection of the next, say SQL, the tags that are linked to docs for those two tags together would be shown, whittling it down so that the user can get closer and closer to his goal.
At the moment all I have is:
List<Tag> _tags = (from t in Tags
where t.allocateTagDoc.Count > 0
select t).ToList();
This is in a method that would be called repeatedly with the optional args as tags were selected.
I think I have been coming at it arse-backwards. If I make two(or more) queries one for each supplied tag, find the docs where they all appear together and then bring out all the tags that go with them... Or would that be too many hits on the db? Can I do it entirely through an entity context variable and just query the model?
Thanks again for any help!
You can try this.
First collect tag to search in a list of strings .
List<string> tagStrings = new List<string>{"c#", "sql"};
pass this list in your query, check whether it is empty or not, if empty, it will return all the tags, else tags which matches the tagStrings.
var _tags = (from t in Tags
where t.allocateTagDoc.Count > 0
&& (tagStrings.Count ==0 || tagStrings.Contains(t.tagName))
select t).ToList();
You can also try this, Dictionary represents ID of a document with it's tags:
Dictionary<int, string[]> documents =
new Dictionary<int, string[]>();
documents.Add(1, new string[] { "C#", "SQL", "EF" });
documents.Add(2, new string[] { "C#", "Interop" });
documents.Add(3, new string[] { "Javascript", "ASP.NET" });
documents.Add(4, new string[] { });
// returns tags belonging to documents with IDs 1, 2
string[] filterTags = new string[] { "C#" };
var relatedTags = GetRelatedTags(documents, filterTags);
Debug.WriteLine(string.Join(",", relatedTags));
// returns tags belonging to document with ID 1
filterTags = new string[] { "C#", "SQL" };
relatedTags = GetRelatedTags(documents, filterTags);
Debug.WriteLine(string.Join(",", relatedTags));
// returns tags belonging to all documents
// since no filtering tags are specified
filterTags = new string[] { };
relatedTags = GetRelatedTags(documents, filterTags);
Debug.WriteLine(string.Join(",", relatedTags));
public static string[] GetRelatedTags(
Dictionary<int, string[]> documents,
string[] filterTags)
{
var documentsWithFilterTags = documents.Where(o =>
filterTags
.Intersect(o.Value).Count() == filterTags.Length);
string[] relatedTags = new string[0];
foreach (string[] tags in documentsWithFilterTags.Select(o => o.Value))
relatedTags = relatedTags
.Concat(tags)
.Distinct()
.ToArray();
return relatedTags;
}
Thought I would pop back and share my solution which was completely different to what I first had in mind.
First I altered the database a little getting rid of a useless field in the allocateDocumentTag table which enabled me to use the entity framework model much more efficiently by allowing me to leave that table out and access it purely through the relationship between Tag and Document.
When I fill my form the first time I just display all the tags that have a relationship with a document. Using my search filter after that, when a Tag is selected in a checkedListBox the Document id's that are associated with that Tag(s) are returned and are then fed back to fill the used tag listbox.
public static List<Tag> fillUsed(List<int> docIds = null)
{
List<Tag> used = new List<Tag>();
if (docIds == null || docIds.Count() < 1)
{
used = (from t in frmFocus._context.Tags
where t.Documents.Count >= 1
select t).ToList();
}
else
{
used = (from t in frmFocus._context.Tags
where t.Documents.Any(d => docIds.Contains(d.id))
select t).ToList();
}
return used;
}
From there the tags feed into the doc search and vice versa. Hope this can help someone else, if the answer is unclear or you need more code then just leave a comment and I'll try and sort it.
I'm currently trying to make a TextBox for my GUI with XNA, and I was wondering how could I find tagged text in a string.
For instanceI have this kind of text:
Hey there, I was <r>going to</r> the <b>Mall</b> today!
So the <r> tag would represent red text and the <b> tag would represent blue text.
And I want to know exactly where the red text starts and where the blue text starts so I could render them separately.
Do you have any suggestion what to do about it, and what to use for doing that?
Thanks in advance.
I would suggest doing this with two methods
First, have a method that can take your string and return a collection of string color pairs:
struct StringColorPair {
public string myText; // the text
public Color myColor; // the color of this text
public int myOffset; // characters before this part of the string
// (for positioning in the Draw)
}
public List<StringColorPair> ParseColoredText(string text) {
var list = new List<StringColorPair>();
// Use a regex or other string parsing method to pull out the
// text chunks and their colors and then for each set of those do:
list.Add(
new StringColorPair {
myText = yourParsedSubText,
myColor = yourParsedColor,
myOffset = yourParsedOffset }
);
return list;
}
Then you would need a draw method like this:
public void Draw(List<StringColorPair> pairs) {
foreach(var pair in pairs) {
// Draw the relevant string and color at its needed offset
}
}
Well you could just parse the line and when you reach a set a color property of your text so that it will now render blue but it will have to be a separate render call or else the whole string will turn blue. So if you make a new string when you come upon a tag then set the color property then render that string then that should work.