I am working with BIRT report, to generate it in xml formate I am writing emitter(BIRT plugin). I need an xml report which contains some repeated sections on the basis of some section in the same report. I am using subReports for that. So the report strcture is like
List--
Header--
Table---(No header and footer)
details---
Cell----
Text(Multiple text here) Some static section and dynamic data
List Details ----
Table--(No header and footer)(This table will be repeated for every record in the header part)
Details--
Cell --
Multiple Text elements ()`
List Footer---- Empty
As I am writing the emitter, I need to extract the the content of all the text elements which are in the Cell elements mainly. Now Following is the code in My emitter for sartCell method.
public void startCell(ICellContent arg0) {
System.out.println("text in Cell:: ");
System.out.println("text in Cell:: " + arg0.getInstanceID());
for(Object ie : arg0.getChildren())
{
if(ie instanceof LabelContent)
{
LabelContent lc = (LabelContent)ie;
stringBuilder.append(lc.getText());
System.out.println(lc.getText());
}
else if(ie instanceof ForeignContent)
{
ForeignContent fc = (ForeignContent)ie;
stringBuilder.append(fc.getRawValue());
System.out.println(fc.getRawValue());
}
}
}
I am able to access the Cell and all it's text content with this logic. But I want to do the same thing with all the cells of the Table in Details section of List. The problem is that Cells in the details section are giving null on ICellContent.getChildren().
And yes My startText method is not getting called at all. Following is the declaration of my emmiter,
public class XmlEmittor extends ContentEmitterAdapter {...}
Following is the rptDesign strcture,
<List>
<Header>
<Table><Detail><Cell><Text>...DynamicText....</Text>.....</Table>
</Header>
<Detail>
<Table><Detail><Cell><Text>...DynamicText....</Text>.....</Table>
</Detail>
</List>
In above structure My text element itself is feeled with XML structure I want to generate the report in. So I just want to append all the content of my xml and write it in outputstream. Please suggest, Thanks in advance
In case of Text elements,
public void startText(ITextContent text)
or
public void startData(IDataContent data) throws BirtException
will be called but in case of Dynamic Text element
public void startForeign(IForeignContent foreign) throws BirtException
will be called.
You can use the below code snippet to get the dynamic text value
#Override
public void startForeign(IForeignContent foreign) throws BirtException {
Object rawValue = foreign.getRawValue();
}
Related
I am new in iText 7, i am developing a spa project (asp.net, c#, and angularjs), where i need to implement a report for existing html page.I found iText 7 (.Net) has a easy way to implement it. Using below code of line, that's return me a byte array and i can easily show in browser as pdf also can download.
var memStream = new MemoryStream();
ConverterProperties converterProperties = new ConverterProperties();
converterProperties.SetFontProvider(fontProvider); converterProperties.SetBaseUri(System.AppDomain.CurrentDomain.BaseDirectory);
HtmlConverter.ConvertToPdf(htmlText, memStream, converterProperties);
In my raw html there has some html tables (every table has some particular rows) and i want to keep them in a page (i mean if table rows not fit in a single page then start from next page). I got a solution like below
Paragraph p = new Paragraph("Test");
PdfPTable table = new PdfPTable(2);
for (int i = 1; i < 6; i++) {
table.addCell("key " + i);
table.addCell("value " + i);
}
for (int i = 0; i < 40; i++) {
document.add(p);
}
// Try to keep the table on 1 page
table.setKeepTogether(true);
document.add(table);
But in my case i cannot implement like that way because content already exist in html tables (in my existing html page).
Advance thanks, if anyone can help me.
This can easily be done using a custom TagWorkerFactory and TableTagWorker class.
Take a look at the code samples below.
The first thing we should do is create a custom TableTagWorker that tells iText to keep the table together. We do this using the code you've mentioned: table.setKeepTogether(true).
class CustomTableTagWorker extends TableTagWorker{
public CustomTableTagWorker(IElementNode element, ProcessorContext context) {
super(element, context);
}
#Override
public void processEnd(IElementNode element, ProcessorContext context) {
super.processEnd(element, context);
((com.itextpdf.layout.element.Table) getElementResult()).setKeepTogether(true);
}
}
As you can see the only thing we changed on our custom TableTagWorker is the fact that it has to keep the table together.
The next step would be to create a custom TagWorkerFactory that maps our CustomTableTagWorker to the table tag in HTML. We do this like so:
class CustomTagWorkerFactory extends DefaultTagWorkerFactory{
#Override
public ITagWorker getCustomTagWorker(IElementNode tag, ProcessorContext context) {
if (tag.name().equalsIgnoreCase("table")) {
return new CustomTableTagWorker(tag, context); // implements ITagWorker
}
return super.getCustomTagWorker(tag, context);
}
}
All we do here is tell iText that if it finds a table tag it should pass the element to the CustomTableTagWorker, in order to be converted to a PDF object (where setKeepTogether == true).
The last step is registering this CustomTagWorkerFactory on our ConverterProperties.
ConverterProperties converterProperties = new ConverterProperties();
converterProperties.setTagWorkerFactory(new CustomTagWorkerFactory());
HtmlConverter.convertToPdf(HTML, new FileOutputStream(DEST), converterProperties);
Using these code samples I was able to generate an output PDF where tables, if small enough to render on an entire page, will never be split across multiple pages.
I had a similar issue of trying to keep together content within a div. I applied the following css property and this kept everything together. This worked with itext7 pdfhtml.
page-break-inside: avoid;
My documents have the property docType that separated them based on the purpose of each type, in the specific case template or audit. However, when I do the following:
document.getProperty("docType").equals("template");
document.getProperty("docType").equals("audit");
The results of them are always the same, it returns every time all documents stored without filtering them by the docType.
Below, you can check the query function.
public static Query getData(Database database, final String type) {
View view = database.getView("data");
if (view.getMap() == null) {
view.setMap(new Mapper() {
#Override
public void map(Map<String, Object> document, Emitter emitter) {
if(String.valueOf(document.get("docType")).equals(type)){
emitter.emit(document.get("_id"), null);
}
}
}, "4");
}
return view.createQuery();
}
Any hint?
This is not a valid way to do it. Your view function must be pure (it cannot reference external state such as "type"). Once that is created you can then query it for what you want by setting start and end keys, or just a set of keys in general to filter on.
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());
I'm struggling to find a proper solution for generating a flat file.
Here are some criteria I need to take care of:
The file has a header with summary of its following records
there could be multiple Collection Header Records with multiple Batch Header Records which contain multiple records of different types.
All records within a Batch have a checksum which has to be added to a batch checksum. This one has to be added to the collection Header checksum and that again to the file checksum. Also each entry in the file has a counter value.
So my plan was to create a class for each record. but what now? I have the records and the "summary records", the next step would be to bring them all in order, count the sums and then set the counters.
How should I proceed from here, should I put everything in a big SortedList? If so, how do I know where to add the latest record (It has to be added to its representing batch summary)?
My first idea was to do something like this:
SortedList<HeaderSummary, SortedList<BatchSummary, SortedList<string, object>>>();
But it is hard to navigate through the HeaderSummaries and BatchSummaries to add a object in the inner Sorted list, bearing in mind that I may have to create and add a HeaderSummary / BachtSummary.
Having several different ArrayLists like one for Header, one for Batch and one for the rest gives me problems when combining them to a flat file because of the order and the - yet to set - counters, while keeping the order etc.
Do you have any clever solution for such a flat file?
Consider using classes to represent levels of your tree structure.
interface iBatch {
public int checksum { get; set; }
}
class BatchSummary {
int batchChecksum;
List<iBatch> records;
public void WriteBatch() {
WriteBatchHeader();
foreach (var record in records)
batch.WriteRecord();
}
public void Add(iBatch rec) {
records.Add(rec); // or however you find the appropriate batch
}
}
class CollectionSummary {
int collectionChecksum;
List<BatchSummary> batches;
public void WriteCollection() {
WriteCollectionHeader();
foreach (var batch in batches)
batch.WriteBatch();
}
public void Add(int WhichBatch, iBatch rec) {
batches[whichBatch].Add(rec); // or however you find the appropriate batch
}
}
class FileSummary {
// ... file summary info
int fileChecksum;
List<CollectionSummary> collections;
public void WriteFile() {
WriteFileHeader();
foreach (var collection in collections)
collection.WriteCollection();
}
public void Add(int whichCollection, int WhichBatch, iBatch rec) {
collections[whichCollection].Add(whichBatch, rec); // or however you find the appropriate collection
}
}
Of course, you could use a common Summary class to be more DRY, if not necessarily more clear.
I'm trying to write a GUI with several JTextFields. Two of the fields are for float values. I already have the fields ignoring any non-numerical inputs via a formatter, but I also want the text in the text field to update to a float value if an integer is inputted (i.e. "5" changes to "5.00"). I tried to update the text field using a setText() command from within an event, but the displayed value is not changing. I want the text to change once focus is lost on the text field.
The code is very long, so I will include the relevant snippet.
heightField.addFocusListener(new FocusAdapter() {
#Override
public void focusLost(FocusEvent arg0) {
heightDone = !heightField.getText().trim().equalsIgnoreCase("");
//This is the problem code//
if(UF.isInt(heightField.getText().trim()))
heightField.setText(heightField.getText().trim().concat(".00"));
System.out.println(heightField.getText());
heightFormat = UF.isFloat(heightField.getText().trim());
isDone();
}
});
maybe you need to refresh the frame after the change on the textField :
frame.repaint();