Some programmatically generated PDFs render incorrectly

Some programmatically generated PDFs render incorrectly

krosier.tx
Participant Participant
1,014 Views
11 Replies
Message 1 of 12

Some programmatically generated PDFs render incorrectly

krosier.tx
Participant
Participant

We have a dedicated Windows 10 workstation with a NVIDIA RTX A4500 GPU that we use to programmatically open Inventor drawings and save a copy of all the sheets to PDF, for storage into our PLM tool. Some drawings end up having malformations with some of their views in the PDF. For example, the masked view should have pink shading but in the PDF the shading is missing, or bubbles have multiple numbers printed on top of each other like this:

 

krosiertx_0-1689174446342.png

 

We generate the PDFs similar to the "Export to PDF API Sample" in the API documentation.

We have Graphics Setting set to Quality on the Hardware tab in the Application Options.

 

The workstation is on our network and its display/video goes out to a KVM Over IP. However, we typically use RDP to connect to it. The Windows user session is initiated via RDP to the workstation upon reboots. The PDFs generate correctly if we leave an RDP session open with the workstation. The issue is consistent only with some drawings, and only while an RDP session is not active with the workstation. Many drawings with bubbles and shading render correctly while nobody is connected to the workstation. Our process is run via a scheduled task configured like this:

 

krosiertx_1-1689174519378.png

 

This is our code simplified to show the essentials for what we are doing. We launch Inventor with the application shown (foreground) and we use the SilentOperation mode to prevent pop-ups from stalling the processing.

 

string drawingPath = @"C:\Users\enguser\Downloads\12345.idw";

// open Inventor
Type invAppType = Type.GetTypeFromProgID("Inventor.Application");
Application app = (Application)Activator.CreateInstance(invAppType);
app.Visible = true;

// prevents Resolve Link and other prompts
app.SilentOperation = true;

// open drawing
Document doc = app.Documents.Open(drawingPath, true);

// create PDF
// create context
TranslationContext tContext = app.TransientObjects.CreateTranslationContext();
tContext.Type = IOMechanismEnum.kFileBrowseIOMechanism;

// setup options
NameValueMap options = app.TransientObjects.CreateNameValueMap();
options.Add("Publish_All_Sheets", true);

// set the destination file name
DataMedium dataMedium = app.TransientObjects.CreateDataMedium();
dataMedium.FileName =  @"C:\Users\enguser\Downloads\12345.pdf";

// create the PDF
translator.SaveCopyAs(doc, tContext, options, dataMedium);

 

We've tried connecting the workstation to a dedicated monitor to rule out the KVM causing the problem, but we can reproduce the issue (again, with certain drawings known to exhibit the behavior).

 

We've tried generating the PDFs this way, but we can still reproduce the issue:

 

// assumes Inventor is already running with a drawing open
Application app = (Application)Marshal.GetActiveObject("Inventor.Application");
Document activeDoc = app.ActiveDocument;

// create PDF
string outputFile = @"C:\Users\enguser\Downloads\12345.pdf";
activeDoc.SaveAs(outputFile, true); // Set the second argument to true to export to a PDF format

 

We started having the issue around the time we upgraded to Inventor 2020 I think. We've since upgraded Inventor a few times and are on 2023 at the moment, and we've upgraded the hardware and OS (Windows 7 to 10) but still see the issue.

 

Autodesk support has been unable to assist us because they don't support custom code, so they recommended we ask the community for help...

0 Likes
1,015 Views
11 Replies
Replies (11)
Message 2 of 12

MjDeck
Autodesk
Autodesk

It sounds like we should be able to reproduce this problem, but it would help if we had a sample drawing.
Can you post one of the drawings that consistently shows the problem (together with associated part or assembly)?
Or if you don't want to post it here, would you be comfortable sending it directly to Autodesk?


Mike Deck
Software Developer
Autodesk, Inc.

0 Likes
Message 3 of 12

krosier.tx
Participant
Participant

Hi Mike,

 

I've been getting inconsistent behavior with this issue in my recent testing. We prepared a drawing that we could share, and I was able to get it to render with pink shading missing while I was disconnected from the workstation. However, when I tried it again a few times it produced the PDF correctly. I'll do some more experimentation and try to provide the bare minimum drawing and procedure that reliably reproduces this behavior.

 

In the meantime, how does Autodesk suggest that users programmatically launch Inventor, open drawings, and export like we're trying to do? The samples that I find in the documentation all assume that Inventor is already running and the drawing is already open. Once I realized that, I did some testing where I would open Inventor like a user would and even open the drawing, then schedule my application (Task Scheduler) to run some minutes in the future. It would grab the running Inventor process and the active document, and then export it to PDF following the code sample I referenced in my original post. I would then close my RDP session to the workstation and connect back after the process finished and review the PDF. Most of the time this was generating correct PDFs but still a few times there would be failures, even with a drawing that worked in a prior test.

 

Thank You,

Kyle

0 Likes
Message 4 of 12

MjDeck
Autodesk
Autodesk

Hi Kyle,

 

 The code you're showing:

 

Application app = (Application)Activator.CreateInstance(invAppType);

 

 

is the recommended way to programatically start Inventor.

There is an extra step you can do after that, but I don't know if it necessary. And it probably won't help with the PDF output problem, because as you say that can happen even when Inventor is already running.
But just because I mentioned it, here's the extra step:

 

        public void WaitForInventorToBeReady()
        {
            bool inventorReady = false;
            while (!inventorReady)
            {
                System.Threading.Thread.Sleep(100);
                System.Windows.Forms.Application.DoEvents();
                if (inventorApp.Ready) inventorReady = true;
            }
			System.Threading.Thread.Sleep(200);
			Debug.Print("InventorInfo - Inventor Ready");
        }

 

 

You could try adding that function, and then calling it after creating the Inventor application and before trying to open the drawing.
But I don't know if that call to System.Windows.Forms.Application.DoEvents(); will work when running from Task Scheduler. You could try inventorApp.UserInterfaceManager.DoEvents() instead.

After you launch Inventor, the code you have to open and export the drawing looks OK.
When you launch Inventor, do you have corresponding code to shut it down? If not, you might have multiple Inventor processes running on the machine.

The task is run from Windows Task Scheduler on the remote computer, right? You're not using Task Scheduler from another computer to run a remote task?


 


Mike Deck
Software Developer
Autodesk, Inc.

0 Likes
Message 5 of 12

MjDeck
Autodesk
Autodesk

This might not make a difference, but here's one thing to try.

On the Drawing tab in Application Options, make sure the "Enable background updates" option (at the bottom of the dialog) is not checked. If you set this once manually (as the enguser user), then it should be persisted in future automated sessions. If this makes a difference, you can code this option into your program so it doesn't have to be set manually.


Mike Deck
Software Developer
Autodesk, Inc.

0 Likes
Message 6 of 12

krosier.tx
Participant
Participant

Thank you for confirming that the way we have been programmatically launching Inventor and opening drawings is correct.

 

Our process does have corresponding code to close Inventor. When we are done verifying different things in the drawing and exporting the PDF we call Close(true) on the Document instance. then we call Quit on the Application instance. The fastest that the process would run again on another drawing would be 40~ seconds later but typically 2+ minutes passes before Inventor is launched again.

 

The process/task is run from Windows Task Scheduler on the remote computer. We do not call it remotely, it runs local on the Windows 10 workstation where Inventor and our C# code runs.

 

I've done some testing where I redesigned our process to just leave Inventor open all the time and just grab a handle for the running process. I've tried then opening the drawing the way we have been:

 

// open drawing
Document doc = app.Documents.Open(drawingPath, true);

 

And like this:

// open the drawing, essentially like a user double-clicking the .idw file
System.Diagnostics.Process.Start(drawingPath);

 

I've still seen some issues with PDFs though. Again, only when nobody is connected to the workstation (via RDP) and our process runs via the scheduled task.


Checking the Ready property of the Application instance is not something that our process does, but it is something that we tried in recent testing. At first I just added a bunch of log output to show that value of the property throughout the different parts of our process. I have seen times where the PDF is generated correctly even though the Ready property is False before and after we call the translator. Despite that, I took this idea a step further and tried subscribing to ApplicationEvents.OnInitializeDocument. The test code waits for this event to fire (kAfter) before proceeding with the translator. This is the simplified code that executes on the main thread:

 

string drawingPath = @"C:\Users\krosier\Downloads\601-TEST Files\601-TEST.idw";

// get a reference to the already running Inventor process
Application app = (Application)Marshal.GetActiveObject("Inventor.Application");
app.SilentOperation = true; // prevents Resolve Link and other prompts

// create object that essentially receives "document is ready" signal
DocumentInitHandler handler = new DocumentInitHandler(drawingPath);
app.ApplicationEvents.OnInitializeDocument += handler.ApplicationEvents_OnInitializeDocument;

// open the drawing, essentially like a user double-clicking the .idw file
System.Diagnostics.Process.Start(drawingPath);

// this thread waits until the handler reports that the document/drawing is open and initialized
while (handler.GetSate() != DocumentState.READY)
{
    System.Threading.Thread.Sleep(500);
}

// get a reference to the active document
Document doc = app.ActiveDocument;

// create the PDF translator
TranslatorAddIn translator = (TranslatorAddIn)app.ApplicationAddIns.ItemById["{0AC6FD96-2F4D-42CE-8BE0-8AEA580399E4}"];
if (!translator.Activated)
{
    translator.Activate();
}

// create context
TranslationContext tContext = app.TransientObjects.CreateTranslationContext();
tContext.Type = IOMechanismEnum.kFileBrowseIOMechanism;

// setup options
NameValueMap options = app.TransientObjects.CreateNameValueMap();
options.Add("All_Color_AS_Black", 0);

// set the destination file name
DataMedium dataMedium = app.TransientObjects.CreateDataMedium();
dataMedium.FileName = @"C:\Users\krosier\Downloads\601-TEST Files\output.pdf";

// create the PDF
translator.SaveCopyAs(doc, tContext, options, dataMedium);

// close the document/drawing
doc.Close(true);

 

Here is the OnInitializeDocument handler that fires on another thread:

 

class DocumentInitHandler
{

    private readonly string DrawingPath;
    private DocumentState DocState = DocumentState.NOT_READY;

    public DocumentInitHandler(string drawingPath)
    {
        DrawingPath = drawingPath;
    }

    public void ApplicationEvents_OnInitializeDocument(_Document DocumentObject, string FullDocumentName, EventTimingEnum BeforeOrAfter, NameValueMap Context, out HandlingCodeEnum HandlingCode)
    {
        if (DrawingPath.Equals(FullDocumentName) && EventTimingEnum.kAfter.Equals(BeforeOrAfter))
        {
            // Inventor reports that the document is initialized
            DocState = DocumentState.READY;
        }
        HandlingCode = HandlingCodeEnum.kEventHandled;
    }

    public DocumentState GetSate()
    {
        return DocState;
    }
}

 

I still see issues with the PDFs of drawings that have had problems with our long standing process, despite the new code only trying the PDF export when the Application is ready and the OnInitializeDocument event fires after the drawing is opened, while nobody is actively connected to the workstation.


I will test the following 2 things and follow up:

  1. We have not tried calling DoEvents() before either way you described, I did not know about that. I will try it and see.
  2.  I will check the "Enable background updates" setting.
0 Likes
Message 7 of 12

krosier.tx
Participant
Participant

I haven't tried adding the DoEvents call yet when I launch Inventor, but I checked the "Enable background updates" setting. We don't have that option selected, and I modified my program to leave Inventor open after it processes the drawing. After one of the times it produced a bad PDF, I checked to see if that option was selected and it was not.

 

Occasionally while testing for this PDF issue I would have a PDF produce correctly for a drawing known to have produced a bad PDF before. Thinking about this, I did a test this morning where I process the same drawing 10 times in a row with a randomized 5-60 second delay between each run. I ran this while nobody was actively connected to the workstation.

 

The files with a size of 12,650 KB are all correct, but the other files have varied problems with different views. There is no consistency with the failures.

 

krosiertx_0-1692022974689.png

0 Likes
Message 8 of 12

krosier.tx
Participant
Participant

Regarding the following suggested call, our application is a console app and not a Windows Forms app. I don't think I can call this.

 

 System.Windows.Forms.Application.DoEvents();

 

I tried the other way that you recommended (inventorApp.UserInterfaceManager.DoEvents()). I got it to work while I was connected to workstation via RDP. Interestingly it takes around 8 seconds to exit the "is ready" loop after launching Inventor, before the drawing is opened. When I tried running our process with this new "is ready" loop, it never exits the loop. The calls to DoEvents complete but the application's ready state is false. I've let it sit for 10~ minutes before killing the process. I can see Inventor displayed (once I RDP'd back to the workstation), and I actually have to go into the task manager to kill the Inventor.exe process even after closing the window I see in the UI. Now that this has happened, I cannot get the "is ready" loop to work even if I run my process while connected to the workstation. I haven't tried rebooting yet.

0 Likes
Message 9 of 12

MjDeck
Autodesk
Autodesk

I wouldn't recommend opening the drawing asynchronously with Process.Start.

Using Documents.Open should be fine. That is synchronous. It won't return until the document is open.
However, even though the document is open that doesn't necessarily mean that all the drawing data is loaded into memory. Before calling the translator, you can call other API code to ensure that data is loaded. This should do it for the balloons:

 

 BalloonDirectionEnum IterateOverBalloons(DrawingDocument drawingDocument) 
        {
            BalloonDirectionEnum dir = BalloonDirectionEnum.kTopDirection;
            foreach(Sheet sheetX in drawingDocument.Sheets) 
            { 
                foreach(Balloon balloonX in sheetX.Balloons)
                {
                    dir = balloonX.PlacementDirection;
                }
            }
            return dir;
        }

 


I looked at your original problem statement again. Can you give more details about "the masked view should have pink shading"? I'm not sure what that means.

If you do use Process.Start, it's better to listen to the OnOpenDocument event. The kAfter of OnOpenDocument will occur after the kAfter of OnInitializeDocument.

Have you tried setting Application.Visible to false? That might make a difference when no display is active.


Mike Deck
Software Developer
Autodesk, Inc.

0 Likes
Message 10 of 12

krosier.tx
Participant
Participant

I switched our code back to using the Application.Documents.Open method to open the drawing after you confirmed that this way was OK. We haven't used the Process.Start method in production, it was just something I tested recently. With our OnInitializeDocument handler I have been seeing that the Documents.Open call returns before OnInitializeDocument fires. With some of our drawings processed yesterday there was a 20 second gap between when Documents.Open returned and when OnInitializeDocument fired. It seemed like it was not synchronous to me so I left in the event handler after I saw this. I will switch our handler to look for the OnOpenDocument event instead of OnInitializeDocument.

 

Regarding the 'pink shading' perhaps the correct terminology is masking and pink is the color that our MEs use? I'm not a typical user of Inventor so forgive my ignorance. We first noticed this PDF issue because the expected shading on a view was missing. The user showed me the view in Inventor with shading (sometimes pink, sometimes gray) and then the PDF (which my program generated) where the same view had no shading. I did some experimenting and realized that when I ran my program to generate the PDF while I was actively connected to the workstation, the shading was there. But when I ran it while nobody was connected, the shading was missing. Since then we started seeing the bubbles issue that I showed a screenshot of, and occasionally a solid black square covering a view or 2D image within a drawing.

 

When I tried running our process with Visible=false on either the Application or the Document (passing false in the .Open call), it seems like Inventor wouldn't open properly. My process would get stuck on something and the Inventor.exe process would only take 12~ MB of RAM (the same amount each time) and never grow from there no matter how long you waited. In the past when we ran Inventor in the background back on Windows 7 I had to do some bonkers thing to get into the interactive mode where Windows processes ran like this. I don't recall the details, but once I got into that mode I could launch Inventor like our scheduled task would and log in with our Autodesk account and get it all working. From then on it would run in this "background" mode without issue. I don't think it's possible to do this with Windows 10 so we haven't revisited it since we first upgraded and ran into problems with it. We found our whole process to be stable with running Inventor in the foreground, as long as an active RDP session remains open with the workstation.

0 Likes
Message 11 of 12

MjDeck
Autodesk
Autodesk

Did you try the IterateOverBalloons code that I posted?
You could also try something similar to iterate over views.


Mike Deck
Software Developer
Autodesk, Inc.

0 Likes
Message 12 of 12

krosier.tx
Participant
Participant

I have not tried it. Are you thinking that some aspect(s) of the drawing is not loading properly when we programmatically open the drawing while nobody is connected to the workstation? I will test iterating over all views and balloons to see if it makes a difference.

 

Thanks!

0 Likes