Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 324167

Summary: BIRT report export to pdf can't generate bookmarks
Product: z_Archived Reporter: Alex Chen <xiaodic>
Component: BIRTAssignee: Yu Chen <yChen>
Status: RESOLVED WORKSFORME QA Contact:
Severity: major    
Priority: P3 CC: bluesoldier, jouyang, xiaodic
Version: 2.5.2   
Target Milestone: 3.7.0   
Hardware: PC   
OS: Windows XP   
Whiteboard: Obsolete

Description Alex Chen CLA 2010-09-01 03:09:54 EDT
Build Identifier: 2.5.2

We use BIRT report engine API to generate pdf file from report library, but now it can't generate bookmarks in pdf under BIRT2.5.2, it works fine with BIRT2.2.2 in Eclipse3.3.2.

I want to know is there any changes in BIRT2.5.2 to generate pdf bookmarks? need I set any options?

Thanks,
--Alex


Our code like below:
   public void doPresentation(IProgressMonitor monitor){
        if(status == IStatus.ERROR) return;
        IReportDocument doc = null;
        try {
//            doc = engine.openReportDocument(docFile);
            doc = ReportDocument.getInstance(engine, docFile, toc);
        } catch (EngineException e1) {
            PublishDocPlugin.getDefault().getLogger().logError(e1);    
        }
        renderTask = engine.createRenderTask(doc);
        RenderOption option = new RenderOption();
//        option.setOption(PDFRenderOption.FIT_TO_PAGE, true);
//        option.setOption(PDFRenderOption.PAGEBREAK_PAGINATION_ONLY, false);
//        option.setOption(PDFRenderOption.HTML_PAGINATION, false);
        output = options.getOutputFilename();
        option.setOutputFileName(output);
        if(options.getOutputFormat()==PublishDocOptions.OUTPUT_FORMAT_PDF)
            option.setOutputFormat(IRenderOption.OUTPUT_FORMAT_PDF);
        else
if(options.getOutputFormat()==PublishDocOptions.OUTPUT_FORMAT_MSWORD)
            option.setOutputFormat("doc"); //$NON-NLS-1$
        option.setSupportedImageFormats("JPG;PNG;BMP;SVG"); //$NON-NLS-1$
//        option.setOption("htmlPagination", Boolean.TRUE); //$NON-NLS-1$
        renderTask.setRenderOption(option);
        monitor.subTask(PublishingDocResource.renderingReport);        
        renderTask.setAppContext(new HashMap());
        Runnable runnable = new Runnable(){                
            public void run() {                    
                try {
//                    Context.enter();
                    renderTask.render();
//                    Context.exit();
                } catch (EngineException e) {                        
                    PublishDocPlugin.getDefault().getLogger().logError(e);      
                }                
            }                            
        };                        
        Thread th = new Thread(runnable);            
        th.start();                        
        while(renderTask.getStatus()==IEngineTask.STATUS_NOT_STARTED ||         
                renderTask.getStatus()==IEngineTask.STATUS_RUNNING){            
            if(monitor.isCanceled()){                    
                renderTask.cancel();                
            }                
            try {                    
                Thread.sleep(100);                
            } catch (InterruptedException e) {                    
                PublishDocPlugin.getDefault().getLogger().logError(e);          
            }            
        }
//        renderTask.close();
        doc.close();
        if(renderTask.getStatus()==IEngineTask.STATUS_SUCCEEDED)
            status = IStatus.OK;
        renderTask = null;
    }

Reproducible: Always
Comment 1 Jun Ouyang CLA 2010-09-01 03:14:42 EDT
Could you please attach a sample document which can reproduce this problem?
Comment 2 Alex Chen CLA 2010-09-01 06:16:09 EDT
(In reply to comment #1)
> Could you please attach a sample document which can reproduce this problem?

1. This issue may relate to this bug: https://bugs.eclipse.org/bugs/show_bug.cgi?id=299737

So I want to know is it OK for below replacement?

/////////////////////////////////
We use below classes before in our code:
1. org.eclipse.birt.report.engine.toc.TOCTree
2. org.eclipse.birt.report.engine.toc.TOCTreeNode

But from we upgrading our product to Eclipse 3.5.1, we found the two above
classes are no longer available, after some investigations, we use below
classes instead:

1. org.eclipse.birt.report.engine.toc.TOCTree ->
org.eclipse.birt.report.engine.toc.TOCView
2. org.eclipse.birt.report.engine.toc.TOCTreeNode ->
org.eclipse.birt.report.engine.toc.document.MemTreeNode

but org.eclipse.birt.report.engine.toc.document.MemTreeNode isn't be exported,
that's why I created this bug.

Or maybe you have some other good suggestions?
//////////////////////////////////////


2. We build TOC on the fly, so I can't upload the document to reproduce this issue, you can get the detail from our code:
doc = ReportDocument.getInstance(engine, docFile, toc);


//////////////////////////////////
public class ReportDocument extends ReportDocumentReader {
	
	private TOCView toc;

	public ReportDocument(IReportEngine engine, IDocArchiveReader archive)
			throws EngineException {
		super((ReportEngine) engine, archive);
	}

	public static IReportDocument getInstance(IReportEngine engine, String docFile, TOCView toc) throws EngineException{
		try {
			IDocArchiveReader reader = new FileArchiveReader(docFile);
			ReportDocument reportDoc = new ReportDocument(engine, reader);
			reportDoc.setToc(toc);
			return reportDoc;
		} catch (IOException e) {
			PublishDocPlugin.getDefault().getLogger().logError(e);
			return null;
		}
	}

	@Override
	public ITOCTree getTOCTree(String format, ULocale locale) {
		if(toc==null) 
			return super.getTOCTree(format, locale);
		if ( !isComplete( ) )
		{
			return null;
		}
		TOCView result = new TOCView((ITreeNode) toc.getRoot( ),
				( (ReportRunnable) getReportRunnable( ) ).getReport( ) , 
				locale, null, format);
		return result;
	}



	public void setToc(TOCView toc) {
		this.toc = toc;
	}

}
//////////////////////////////////
Comment 3 Alex Chen CLA 2010-09-01 06:18:06 EDT
If you could, please point me to the BIRT code location where generate the bookmarks, then I can debug it?

Thanks much,
--Alex
Comment 4 Alex Chen CLA 2010-09-06 23:10:09 EDT
Any updates on this issue?

Thanks,
--Alex
Comment 5 Jun Ouyang CLA 2010-09-07 00:54:19 EDT
Alex,

Please try to use ITreeNode instead of TOCTreeNode/MemTreeNode to avoid compatibility issue.

What do you mean by "build TOC on the fly"? Is there some sample program(runnable for us) to reproduce your problem?
Comment 6 Alex Chen CLA 2010-09-07 05:14:12 EDT
(In reply to comment #5)
> Please try to use ITreeNode instead of TOCTreeNode/MemTreeNode to avoid
> compatibility issue.

ITreeNode is an interface, to build TOC by ourselves, we need a concrete class which implements ITreeNode, right?

Previously, we use 
1. org.eclipse.birt.report.engine.toc.TOCTree
2. org.eclipse.birt.report.engine.toc.TOCTreeNode

But in BIRT 2.5.2, they are gone, so could you please tell me which classes replaced them?

> 
> What do you mean by "build TOC on the fly"? Is there some sample
> program(runnable for us) to reproduce your problem?

I mean we build TOC by ourselves, instead of let BIRT build it as default. 

public class ReportTOCBuilder {

	TreeNode root = null;
	TOCView tree = null;

	public ReportTOCBuilder(ReportDesignHandle design){
		this.root = new MemTreeNode();
		this.tree = new TOCView(root, design, ULocale.getDefault(), null, "" ); //$NON-NLS-1$

//		public TOCView( ITreeNode tree, ReportDesignHandle handle, ULocale locale,
//				TimeZone timeZone, String format )
		
	}

	public TOCView getTOCTree(){
		return this.tree;
	}

	public MemTreeNode getRoot(){
		return (MemTreeNode)this.root;
	}

	public MemTreeNode createNode(MemTreeNode parentNode, String bookmark, Object tocValue){
		String id = parentNode.getNodeId( );
		if ( id == null )
		{
			id = "__TOC"; //$NON-NLS-1$
		}
		id = id + "_" + parentNode.getChildren( ).size( ); //$NON-NLS-1$

		MemTreeNode node = new MemTreeNode();
		node.setNodeId( id );
		node.setBookmark( bookmark == null ? id : bookmark );
		node.setParent( parentNode );
		node.setTOCValue(tocValue);
		parentNode.getChildren( ).add( node );
		return node;
	}

}

Again, could you point me the location in BIRT engine to generate the pdf bookmarks?

Thanks,
--Alex
Comment 7 Yu Chen CLA 2010-09-07 05:40:02 EDT
In pdf output, the TOC is generated in org.eclipse.birt.report.engine.emitter.pdf.PDFPageDevice.createTOC(...).
It need not any extra options.
Comment 8 Jun Ouyang CLA 2010-09-07 22:26:23 EDT
1. The TOC tree is generated by TOCBuilder.

2. In PDF, the TOCs are output by PDFPageDevice.createTOC(), bookmarks are output by PDFPage.createBookmark(), both classes are in plugin org.eclipse.birt.report.engine.emitter.pdf. Please notice TOC and bookmark are different.

3. According to my understanding, you create your own TOCBuilder or TOC writer, is that correct? If so, you can just copy the code MemTreeNode and use it as your own class. So that it's not necessary to depends on the class of BIRT and can avoid compatibility issue.
Comment 9 Alex Chen CLA 2010-09-08 02:19:02 EDT
Hi BIRT guys,

Seems BIRT changed its API in org.eclipse.birt.report.engine.api.impl.ReportDocumentReader, right now I use org.eclipse.birt.report.engine.api.impl.ReportDocumentReader.getTOCTree() instead of org.eclipse.birt.report.engine.api.impl.ReportDocumentReader.getTOCTree(String, ULocale), then I get right result.

	synchronized public ITreeNode getTOCTree( ) throws EngineException
	{
		if ( !isComplete( ) )
		{
			return null;
		}
		
		return this.treeRoot;
	}

So please resolve this issue, and thanks very much for your kindly help!

--Alex
Comment 10 Jun Ouyang CLA 2010-09-08 03:35:03 EDT
Resolved as "worksforme".