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:


Subscribe via RSS
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.
Posted by: ben | September 29, 2007 at 06:10 AM
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
Posted by: Kean | October 01, 2007 at 08:01 PM
"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?
Posted by: Sony | November 29, 2007 at 05:52 AM
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!
Posted by: Thomas | February 18, 2008 at 04:37 PM
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
Posted by: Kean | February 19, 2008 at 06:28 AM
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
Posted by: Eric | April 17, 2008 at 08:15 PM
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
Posted by: Kean | April 21, 2008 at 11:44 AM
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.
Posted by: Sparky | August 06, 2008 at 06:43 PM
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
Posted by: Kean | August 06, 2008 at 11:10 PM
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
Posted by: Ray L'Amoreaux | October 02, 2008 at 03:50 PM
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
Posted by: Kean | October 04, 2008 at 04:29 PM
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?
Posted by: Nick Lee | November 25, 2008 at 08:08 PM
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
Posted by: Kean | November 26, 2008 at 06:48 AM
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
Posted by: Peter | December 20, 2008 at 03:05 AM
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.
Posted by: Todd | December 30, 2008 at 11:09 PM