A blog for developers programming with Autodesk platforms, particularly AutoCAD and Forge. With a special focus on AR/VR and IoT.

September 29, 2007

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

{

publicclassPlottingCommands

{

[CommandMethod("mplot")]

staticpublicvoid 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 = newPlotInfo();

PlotInfoValidator piv =

newPlotInfoValidator();

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 =

newPlotProgressDialog(false, 1, true);

using (ppd)

{

ObjectIdCollection layoutsToPlot =

newObjectIdCollection();

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 =

newPlotSettings(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 = newPlotPageInfo();

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 =

newObjectIdCollection();

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 =

newPlotProgressDialog(

false,

layoutsToPlot.Count,

true

);

using (ppd)

{

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