Driving a multi-sheet AutoCAD plot using .NET

Somewhat symmetrically I'm posting this from Chicago airport, once again, but thankfully I'm now on my way home. It was a busy week of meetings, but I did get the chance to put together some code that extended the last post into the realm of multi-sheet plot jobs.

The following code took some work, but I finally managed to iron out the obvious wrinkles and put together an approach to plot multiple sheets into a single document. The standard DWF6 driver doesn't appear to support multiple sheet jobs (directly, at least), so I chose to use the DWFx driver that I probably downloaded and installed from here.

I haven't "diffed" and colour-coded the changed lines with the previous post, as there ended up being quite a lot of swapping around etc., but you should be able to perform that comparison yourself, if you so wish.

Here's the C# code:

using Autodesk.AutoCAD.Runtime;

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Geometry;

using Autodesk.AutoCAD.PlottingServices;

namespace PlottingApplication

{

  public class PlottingCommands

  {

    [CommandMethod("mplot")]

    static public void MultiSheetPlot()

    {

      Document doc =

        Application.DocumentManager.MdiActiveDocument;

      Editor ed = doc.Editor;

      Database db = doc.Database;

      Transaction tr =

        db.TransactionManager.StartTransaction();

      using (tr)

      {

        BlockTable bt =

          (BlockTable)tr.GetObject(

            db.BlockTableId,

            OpenMode.ForRead

          );

        PlotInfo pi = new PlotInfo();

        PlotInfoValidator piv =

          new PlotInfoValidator();

        piv.MediaMatchingPolicy =

          MatchingPolicy.MatchEnabled;

        // A PlotEngine does the actual plotting

        // (can also create one for Preview)

        if (PlotFactory.ProcessPlotState ==

            ProcessPlotState.NotPlotting)

        {

          PlotEngine pe =

            PlotFactory.CreatePublishEngine();

          using (pe)

          {

            // Create a Progress Dialog to provide info

            // and allow thej user to cancel

            PlotProgressDialog ppd =

              new PlotProgressDialog(false, 1, true);

            using (ppd)

            {

              ObjectIdCollection layoutsToPlot =

                new ObjectIdCollection();

              foreach (ObjectId btrId in bt)

              {

                BlockTableRecord btr =

                  (BlockTableRecord)tr.GetObject(

                    btrId,

                    OpenMode.ForRead

                  );

                if (btr.IsLayout &&

                    btr.Name.ToUpper() !=

                      BlockTableRecord.ModelSpace.ToUpper())

                {

                  layoutsToPlot.Add(btrId);

                }

              }

              int numSheet = 1;

              foreach (ObjectId btrId in layoutsToPlot)

              {

                BlockTableRecord btr =

                  (BlockTableRecord)tr.GetObject(

                    btrId,

                    OpenMode.ForRead

                  );

                Layout lo =

                  (Layout)tr.GetObject(

                    btr.LayoutId,

                    OpenMode.ForRead

                  );

                // We need a PlotSettings object

                // based on the layout settings

                // which we then customize

                PlotSettings ps =

                  new PlotSettings(lo.ModelType);

                ps.CopyFrom(lo);

                // The PlotSettingsValidator helps

                // create a valid PlotSettings object

                PlotSettingsValidator psv =

                  PlotSettingsValidator.Current;

                // We'll plot the extents, centered and

                // scaled to fit

                psv.SetPlotType(

                  ps,

                Autodesk.AutoCAD.DatabaseServices.PlotType.Extents

                );

                psv.SetUseStandardScale(ps, true);

                psv.SetStdScaleType(ps, StdScaleType.ScaleToFit);

                psv.SetPlotCentered(ps, true);

                // We'll use the standard DWFx PC3, as

                // this supports multiple sheets

                psv.SetPlotConfigurationName(

                  ps,

                  "DWFx ePlot (XPS Compatible).pc3",

                  "ANSI_A_(8.50_x_11.00_Inches)"

                );

                // We need a PlotInfo object

                // linked to the layout

                pi.Layout = btr.LayoutId;

                // Make the layout we're plotting current

                LayoutManager.Current.CurrentLayout =

                  lo.LayoutName;

                // We need to link the PlotInfo to the

                // PlotSettings and then validate it

                pi.OverrideSettings = ps;

                piv.Validate(pi);

                if (numSheet == 1)

                {

                  ppd.set_PlotMsgString(

                    PlotMessageIndex.DialogTitle,

                    "Custom Plot Progress"

                  );

                  ppd.set_PlotMsgString(

                    PlotMessageIndex.CancelJobButtonMessage,

                    "Cancel Job"

                  );

                  ppd.set_PlotMsgString(

                    PlotMessageIndex.CancelSheetButtonMessage,

                    "Cancel Sheet"

                  );

                  ppd.set_PlotMsgString(

                    PlotMessageIndex.SheetSetProgressCaption,

                    "Sheet Set Progress"

                  );

                  ppd.set_PlotMsgString(

                    PlotMessageIndex.SheetProgressCaption,

                    "Sheet Progress"

                  );

                  ppd.LowerPlotProgressRange = 0;

                  ppd.UpperPlotProgressRange = 100;

                  ppd.PlotProgressPos = 0;

                  // Let's start the plot, at last

                  ppd.OnBeginPlot();

                  ppd.IsVisible = true;

                  pe.BeginPlot(ppd, null);

                  // We'll be plotting a single document

                  pe.BeginDocument(

                    pi,

                    doc.Name,

                    null,

                    1,

                    true, // Let's plot to file

                    "c:\\test-multi-sheet"

                  );

                }

                // Which may contain multiple sheets

                ppd.StatusMsgString =

                  "Plotting " +

                  doc.Name.Substring(

                    doc.Name.LastIndexOf("\\") + 1

                  ) +

                  " - sheet " + numSheet.ToString() +

                  " of " + layoutsToPlot.Count.ToString();

                ppd.OnBeginSheet();

                ppd.LowerSheetProgressRange = 0;

                ppd.UpperSheetProgressRange = 100;

                ppd.SheetProgressPos = 0;

                PlotPageInfo ppi = new PlotPageInfo();

                pe.BeginPage(

                  ppi,

                  pi,

                  (numSheet == layoutsToPlot.Count),

                  null

                );

                pe.BeginGenerateGraphics(null);

                ppd.SheetProgressPos = 50;

                pe.EndGenerateGraphics(null);

                // Finish the sheet

                pe.EndPage(null);

                ppd.SheetProgressPos = 100;

                ppd.OnEndSheet();

                numSheet++;

              }

              // Finish the document

              pe.EndDocument(null);

              // And finish the plot

              ppd.PlotProgressPos = 100;

              ppd.OnEndPlot();

              pe.EndPlot(null);

            }

          }

        }

        else

        {

          ed.WriteMessage(

            "\nAnother plot is in progress."

          );

        }

      }

    }

  }

}

The output of the MPLOT command will be created in "c:\test-multi-sheet.dwfx", which can then be viewed using Autodesk Design Review 2008 or the XPS viewer that ships with Windows Vista or from here for Windows XP.

Update

I spent some more time looking at this code and noticed a minor issue... We need to tell the plot dialog that we're working with multiple sheets in its constructor. So we first need to count the sheets and then create the dialog. Here's the modified section of code:

          PlotEngine pe =

            PlotFactory.CreatePublishEngine();

          using (pe)

          {

            // Collect all the paperspace layouts

            // for plotting

            ObjectIdCollection layoutsToPlot =

              new ObjectIdCollection();

            foreach (ObjectId btrId in bt)

            {

              BlockTableRecord btr =

                (BlockTableRecord)tr.GetObject(

                  btrId,

                  OpenMode.ForRead

                );

              if (btr.IsLayout &&

                  btr.Name.ToUpper() !=

                    BlockTableRecord.ModelSpace.ToUpper())

              {

                layoutsToPlot.Add(btrId);

              }

            }

            // Create a Progress Dialog to provide info

            // and allow thej user to cancel

            PlotProgressDialog ppd =

              new PlotProgressDialog(

                false,

                layoutsToPlot.Count,

                true

              );

            using (ppd)

            {

This now leads to the plot progress dialog showing multiple progress bars:

Multisheet_plot_progress

  1. Nice code!
    By the way, can you give an example about how to draive a multi-sheet AutoCAD plotpreview using .NET? I try it by myself but have no way. Maybe the PreviewEndPlotInfo Class is helpful, but how to use this class?
    very thanks.

  2. Sure thing. Today's post looks at single-sheet previewing, while a follow-up post later in the week will look at multi-sheet.

    Kean

  3. "Operation is not valid due to the current state of the object."

    i am getting this error while running the above code in autocad 2008.it will plot first sheet layout but when it loops back for second sheet it throws this error.

    but the above code works fine for autocad 2006..

    any solution kean?

  4. Many thanks for the code sample!

    Is it also possible to plot the same layout multiple times? I've got a huge drawing and defined some sheet lines there. Now I want to plot a set of these sheets into one dwg or pdf file. I modified your code accordingly and am iterating through every sheet instead of the "layoutsToPlot". In the loop, I'm zooming to the extents of the current sheet. Unfortunately, the resulting file contains only one page (the one of the last sheet). Am I on the right track with this approach? Which command actually tells AutoCAD to start a new page (has to be another one than pe.BeginPage)?

    Your help is highly appreciated!

  5. It's hard to know exactly what's happening, based on the description, but one suggestion: make sure you're using a multi-sheet compatible plotter & driver...

    Kean

  6. Thanks so much for this example.

    I do have a question. I have implemented the code and am using it to plot several drawing files with multiple layouts to PDFs but there seems to be an issue. I am waiting for the PlotFactory.ProcessPlotState to return the NotPlotting status before I start the next drawing. This takes a very long time to return. Is there a better way to accomplish this besides waiting on the NotPlotting status to return before I start the next plot cycle?

    Any assistance is appreciated.

    Eric

  7. Hi Eric,

    The logical way would be to rely on Background Plotting to avoid the delay. I can't remember whether I tested it with this code, though.

    I'd suggest sending a reproducible sample through to the ADN team, referencing this post.

    Regards,

    Kean

  8. Hi Kean,

    Do you know how I can get a multi-page DWF file to label all the sheets? I currently have multiple DWG files that I open and create a single multi-page DWF (via C#), but only the first sheet has a name. I can't see where I should be passing names for the other sheets. Thanks.

  9. Hi Sparky,

    Sorry - I don't know the answer to this one, off the top of my head (and I don't have time to research it, either, right now).

    I'd suggest asking via ADN or the AutoCAD .NET Discussion Group.

    Regards,

    Kean

  10. Kean,

    First, I'm amazed at the work you do and how quickly you come up with all these examples. Very helpful. Thanks.

    However, I am having the same problem as Sony:

    Operation is not valid due to the current state of the object.

    (AutoCAD - Civil 3d 2009).

    When the tr.GetObject tries to access the 2nd layoutsToPlot using BTR Id, AutoCAD crashes with the above error.

    I use exactly the same code as in more than one of the examples...

    Any ideas for me to try?

    Thanks Again.

    Ray L'Amoreaux
    FDOT

  11. Ray,

    Thanks for the kind words.

    Do you see this behavior in every drawing? If so, is there something specific about the drawing setup?

    Feel free to email me (or, ideally, submit it via ADN) a drawing which reproduces the problem consistently with no change in the above code.

    Kean

  12. Hey Kean,

    Love your stuff, hopefully see you in vegas next week. I am having the same trouble as the previous fellas while trying to print:

    "Operation is not valid due to the current state of the object"

    Did you come to a conclusion on this?

  13. Hi Nick,

    I don't remember seeing anything come through from Ray (although he may have submitted via ADN).

    I'll repeat the offer I made to Ray: if you can send me a drawing and clear steps to reproduce the problem, I'll take a look (after AU :-).

    Be sure to say Hi if you see me in Vegas!

    Kean

  14. Does Anybody have an idea why am I getting only one Layout (the last one printed) showing up in the dwfx file?
    If I use a pdf printer then I have all may layouts.
    I am using the code shown above translated to VB.net.

    Thanks for your help.
    Peter

  15. I'm having the same problem as the others. I've tried both C# and converted it over to .Net and it's failing upon reaching the second sheet at

    BlockTableRecord btr =
    BlockTableRecord)tr.GetObject(
    btrId,
    OpenMode.ForRead

    My company won't spring for a membership to ADN, so I've no way to submit the files there, but if you have other ways to get it to you, then I'm more than happy to send them along.
    I'm using C3d 2009 SP1 here along with VS 2008 Express edition.

  16. Ognyan Dimitrov Avatar
    Ognyan Dimitrov

    Hi all,
    I have problem changing the plot resolution.
    I have managed to make multiple sheets in PDF format with just a line changed, but in PDF text appears very bad in low resolutions. I looked in the Managed Class Reference and searched the objects to see if there is a way. The only thing I found was CustomShadeDPI property, but does not seem to work.

    If anybody have an idea or give me just a hint where to read!
    Thanks.

  17. Kean Walmsley Avatar

    Hi there,

    I'm afraid I don't know how best to affect this, although someone on the AutoCAD .NET Discussion Group might.

    Regards,

    Kean

  18. Hi,

    I've problem because i need to change Layer state between 2 sheets plotting, but just the last state is printed in all sheets. Can anyone help me.

    Thanks

  19. Kean Walmsley Avatar

    Hi Thib,

    This isn't a forum for support.

    Your comment doesn't appear to relate to this post, so please submit your question to the ADN team, if you're a member, or otherwise the AutoCAD .NET Discussion Group.

    Kean

  20. Hi everyone,

    Has anyone been able to get this (or the similar code found in the AutoCAD .NET Developer's Guide) to work with AutoCAD 2010? I'm able to do a successful plot when plotting to a system printer, but not when printing to DWF6 or DWFx.

    Thanks,

  21. Okay,

    I got the plot to output to a file. There must have been some security issue associated with writing to the C:\ directory (I'm using Windows 7). By plotting to another directory (E.G., C:\Temp\) I was able to produce the output file.

    Another question, though: Why doesn't the output file contain each of the drawing layouts? The code attempts to plot each layout, but the only layout to show up in the output file is the first layout plotted.

    Thanks,

  22. Please let me know what driver you're getting this behaviour with (and on which version of AutoCAD). I'll see if I can reproduce it, when I get some time (which I unfortunately don't have much of, right now).

    Kean

  23. Hi Kean,

    Thanks for the response.

    I'm using AutoCAD 2010 (which is part of the Inventor / Mechanical suite) with the DWFx ePlot (XPS Compatible).pc3 driver.

  24. Hello,

    Has anyone gotten this example to function correctly? When I execute the command, I only get one layout to show up in the DWFx document.

    Windows 7
    AutoCAD/Inventor 2010
    Visual Studio 2008

  25. There seems to be an issue with the DWFx driver in AutoCAD 2010 accepting multiple sheets: I had the same issue originally with the DWF6 driver (although it could also have been a different issue), while the DWFx driver worked. Now the DWFx driver just prints a single layout, which other multi-page enabled drivers work fine.

    So either there's something I'm missing from this code, or it's only possible to drive certain multi-page drivers.

    I'll try to take another look at this, but it may take some time for me to get to the bottom of it.

    Kean

  26. A member of my team is looking into this... I'll report back once we know what the situation.

    In case you have an urgent requirement, driving a multi-sheet plot using a DSD-based approach does work with DWFx (although it clearly requires a different approach in your code).

    Kean

  27. Hi Michael,

    There seems to be a problem with the DWFx ePlot driver in AutoCAD 2010 (either that or the code needs updating to be able to use it).

    Someone in my team is looking into this...

    Kean

  28. It turns out that the DWFx driver - as integrated into AutoCAD - does not support multi-sheet plotting.

    Which means that to plot multiple sheets to DWFx you really need to use a DSD-based approach that uses Publish to create the file.

    I'll work on putting something together to demonstrate this.

    Kean

    1. I apologize if its a silly question, but what does DSD mean? 🙂

      1. Kean Walmsley Avatar

        DSD is the file format the PUBLISH command uses. Not sure what it stands for...

        Kean

  29. any updates Kean? It's not working on 2011 either.

    Thanks,

  30. What specifically are you looking for? Is it related to the issues with the DWFx driver?

    Kean

  31. I am looking to automate the export command, so I applied the above code, on the DWFx driver, but I only get one layout exported.

    btw, thanks for the great post.

  32. Hi Moe,

    Sorry - no change that I'm aware of. The DWFx driver is now single-sheet.

    Regards,

    Kean

  33. Thank you Kean...

  34. Hi Kean,
    I try change paper size when multi-sheet plotting, but eInvalidPlotInfo error occur.
    is it possible?
    Thanks,

  35. Kean Walmsley Avatar

    Sorry - I don't know the answer, off the top of my head, and don't have time to investigate.

    Please submit your question to the ADN team, if you're a member, or otherwise the AutoCAD .NET Discussion Group.

    Kean

  36. hi!
    i found an error in the autocad api.
    if you try to plot multiple layouts with different paperformats, you'll get an error at the line
    pe.BeginPage(ppi, pi, (numSheet == layoutsToPlot.Count), null);
    named eInvalidPlotinfo

    because when you call the function pe.BeginDocument you use the PlotInfo Object from the first Layout
    pe.BeginDocument(pi, doc.Name, null, 1, true, Exportpath + NewFileNameWithoutExtension);

    if all Layouts have the same Paperformat, everything is fine. so this function does not work properly everytime.

  37. hi!
    Confirm your problem.Don't work with different paper size.I hope that someone have a workaround or Autodesk have to update NET API.

  38. hi Kean,
    i have the same problem.Don't work with different paper size.I hope that you have a workaround or Autodesk have to update NET API.
    I'm waiting your answer.
    Thanks.
    (I use acad2011 32 bit and VB.net)

  39. Your best bet is to submit this via ADN or to the AutoCAD .NET Discussion Group (I don't have time to follow up on issues such as this, unfortunately).

    Kean

  40. Solution: I did was delete the corresponding line ('ps.CopyFrom (lo)) and the error is solved.

    so no matter PaperFormat have the layaouts.

    at least I funicona me correctly

  41. This does not work in AutoCAD 2016.
    It close down AutoCAD what is shall make the second page, pe.BeginGenerateGraphics(null). >> error in accoremgd.dll.
    It worked fin in 2013 and 2014, but not in 2016. Is the API changed?

    1. It works for me in 2016. I'll need more information to reproduce the problem (I suggest also checking on different systems, to make sure it's not a problem with your installation).

      Kean

      1. I use 2016 with SP1 (that SP1 made quite a lot changes that was not supposed to happen, for both VBA and NET >> waiting for SP2 so we can start using it). I have tested on three different computers with same result.

        1. I'm also using SP1, so that isn't necessarily the issue...

          Kean

        2. Ours were working on 2016 before we installed SP1. Now it closes AutoCAD when making the second page at the line pe.BeginGenerateGraphics(null)

      2. Hello Kean,
        I am also facing same issue with AutoCAD 2016 64 bit (Windows 7 64 bit)
        Please refer discussion at forums.autodesk.com/

        Please please give me the solution. Unable to work on AutoCAD 2016

        1. My OS Windows 7 64 bit SP1 but still have same issue

          1. Kean Walmsley Avatar

            The code as it stands in the post still works for me on AutoCAD 2016 SP1. I'm very busy this week at the Forge DevCon, so hopefully someone will help you via the forum.

            Kean

            1. Ok Kean, I tried same code on the other machine having similar install but it works perfectly without crash. Can you please suggest. What may have went wrong with my machine which produces (nice !) crash

              1. Kean Walmsley Avatar

                No idea - I suggest reinstalling AutoCAD, to start with.

                Kean

    2. Try set BACKGROUNDPLOT to 0, it works for me in Autocad 2016 SP1

  42. Multisheet plot publishes PDF file. But pages of PDF must have name of layout name. How to set PDF page name equal to layout name respectively?
    Thanks in advance

    1. Please post your question to the AutoCAD .NET forum.

      Thank you,

      Kean

  43. how we can read all the entities of currently open drawings?

  44. Kean,

    Thanks so much for your posts, I've learned so much about programming plugins in C# in AutoCAD here.

    I've attempted to modify your code to plot a DWG that is not currently open in AutoCAD, since there could be hundreds to process at a time. I have a Windows form that opens within the AutoCAD environment and has a multi-select file dialog box to allow batch-plotting, and want to send the drawing path along with a revision number to a method based on this code.

    I'm running into an error "eLayoutNotCurrent", which appears to be happening because the DWG is not the current layout (since it isn't open). I'm attempting to plot the only layout in paper space for each DWG I pass to this method.

    What is my best approach to remedy this or work-around the limitation? Please see code in my pastebin below:
    pastebin.com/GmgTy79D

    1. This is an old post, and I no longer work with AutoCAD. Please post your question to the AutoCAD .NET forum.

      Thanks,

      Kean

Leave a Reply to pushpakp Cancel reply

Your email address will not be published. Required fields are marked *