I just missed my connecting flight in Chicago, so have 3 hours to pass until the next, and decided to post some code I finally got around to writing on the plane from Zurich.
I've had a few requests for code showing how to plot using the .NET API in AutoCAD. There's an existing ObjectARX (C++) sample on the SDK, under samples/editor/AsdkPlotAPI, but there isn't a publicly posted .NET version right now.
Here's the C# code I put together. Please bear in mind that it was written during less than ideal coding conditions, and I haven't spent a substantial amount of time going through it... it seems to work fine for me, but do post a comment if you have trouble with it.
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("simplot")]
static public void SimplePlot()
{
Document doc =
Application.DocumentManager.MdiActiveDocument;
Editor ed = doc.Editor;
Database db = doc.Database;
Transaction tr =
db.TransactionManager.StartTransaction();
using (tr)
{
// We'll be plotting the current layout
BlockTableRecord btr =
(BlockTableRecord)tr.GetObject(
db.CurrentSpaceId,
OpenMode.ForRead
);
Layout lo =
(Layout)tr.GetObject(
btr.LayoutId,
OpenMode.ForRead
);
// We need a PlotInfo object
// linked to the layout
PlotInfo pi = new PlotInfo();
pi.Layout = btr.LayoutId;
// 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 DWF PC3, as
// for today we're just plotting to file
psv.SetPlotConfigurationName(
ps,
"DWF6 ePlot.pc3",
"ANSI_A_(8.50_x_11.00_Inches)"
);
// We need to link the PlotInfo to the
// PlotSettings and then validate it
pi.OverrideSettings = ps;
PlotInfoValidator piv =
new PlotInfoValidator();
piv.MediaMatchingPolicy =
MatchingPolicy.MatchEnabled;
piv.Validate(pi);
// 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)
{
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-output"
);
// Which contains a single sheet
ppd.OnBeginSheet();
ppd.LowerSheetProgressRange = 0;
ppd.UpperSheetProgressRange = 100;
ppd.SheetProgressPos = 0;
PlotPageInfo ppi = new PlotPageInfo();
pe.BeginPage(
ppi,
pi,
true,
null
);
pe.BeginGenerateGraphics(null);
pe.EndGenerateGraphics(null);
// Finish the sheet
pe.EndPage(null);
ppd.SheetProgressPos = 100;
ppd.OnEndSheet();
// 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."
);
}
}
}
}
}
A few comments on the code: I chose to plot to a file using the standard DWF plot driver/PC3 file, mainly as it avoids having to second-guess what printers/plotters everyone uses. :-) The SDK sample populates comboboxes in a dialog to allow selection of the device and the media size, but I've just gone with a choice that should work on all systems.
Here's what you should see when you launch the SIMPLOT command:
The output file should be created in "c:\test-output.dwf".
I added a simple check to fail gracefully when another plot is in progress... as this code will drive either a foreground or a background plot (depending on the value of the BACKGROUNDPLOT system variable), there's clear scope for failure if the code doesn't check via PlotFactory.ProcessPlotState (or handle the exception if another plot is already happening when you call PlotFactory.CreatePublishEngine()).
Now that I'm online I've found there's quite a similar code sample to the ADN site (also converted from the original SDK sample, I suspect):


Subscribe via RSS
Sir,
I am Akash, pursuing Masters in Construction Management from Indian Institute of Technology- Madras, Chennai (India)
I had been using the VB 6.0 version till date and still would be continuing my work with that..
I have an application to be coded where in 1) I have a set of coordinates say 6 coordinates for a cuboidal shape in excel sheet.
2) I want to use them in Autocad to plot a drawing (every cuboid) based on these coordinates by importing from Excel file.
3) I have a series of such cuboids to be connected to make a long span. I want this Plot to be finally displayed on VB form where my application is there.
I look forward to receive some useful information from you at the earliest..
PS: I have a bigger constraint in switchhing over to .Net for my application hence, please keep VB 6.0 in mind..
Posted by: Akash | March 05, 2008 at 08:30 AM
Akash - please have your institute join ADN (if they're not already a member) or submit your question to one of the discussion groups.
Regards,
Kean
Posted by: Kean | March 05, 2008 at 09:10 AM
G'day Kean
Thanks for a great site.
I am having trouble doing consecutive prints from one method call. I am trying to produce a DWF and a PDF of a drawing, but the second plot fails because the first plot does not start immediately. I have tried sleeping the thread and checking the ProcessPlotState. I also tried starting the plot in a separate thread, but that is crashing AutoCAD (even with try/catch blocks).
Any ideas?
Regards
Michael
Posted by: Michael Csikos | May 20, 2008 at 07:35 AM
Hi Michael,
You probably need to make sure background plotting is disabled. If the BACKGROUNDPLOT sysvar is set to 1 or 3, then PLOT will background plot (which is not what you want).
Cheers,
Kean
Posted by: Kean | May 20, 2008 at 09:05 AM
Thanks Kean, that did the trick.
Posted by: Michael Csikos | May 28, 2008 at 06:38 AM
Dear Kean,
I'm currently working on VBA for AutoCAD Mechanical 2007. I wonder if there is a way to edit the pc3/pmp files programatically from this interface. The goal is to enable the users to create custom sized flowsheets without manually add the Papersize to the pc3.
So I'd like to create a mask, where the user types the desired width (height is given by the rolls in the plotter...) and they get their layout correctly sized. Wath I did as workaround is that I set up a 15 m long papersize for all Paperrolls and set the Cutting mode to extents. The only problem is, if I set up a Page Layout, users will get the whole 15 m as paper and this will be confusing I think... So can I access these settings programatically from VBA?
Thanks in advance,
Daniel
Posted by: Daniel Balogh | September 11, 2008 at 07:46 AM
Dear Daniel,
The recommended way of changing plot configurations is via the API, not by modifying PC3 files. This API is exposed via C++ (ObjectARX) and .NET.
I don't know of a way to access the API directly from VBA - you may need to include a C++ or .NET module in your application for this.
Regards,
Kean
Posted by: Kean | September 11, 2008 at 08:24 AM
I'm writing some code for automatic PDF plotting (Won't go into details) but I'm new to AutoCAD.Net and I've been having problems adapting your code.
I've tried stipping back my changes up to the point where it is basically a copy and paste of the above code without the progress bar in a hope to find what's wrong and I've made it to the point where even though my code is exactly the same I get this error: Autodesk.AutoCAD.Runtime.Exception: eInvalidInput
at Autodesk.AutoCAD.DatabaseServices.PlotSettingsValidator.SetPlotConfigurationName(PlotSettings plotSet, String plotDeviceName, String mediaName)
My code for that line being:
Psv.SetPlotConfigurationName(Ps, "PDF Creator - A1.pc3", "A1");
Any ideas?
Posted by: Andrew | January 05, 2009 at 06:21 AM
My best suggestion would be to double-check your assumptions by testing your code against another device/driver.
Kean
Posted by: Kean Walmsley | January 12, 2009 at 09:44 AM
I've made it to the point where this all works except for the actual plot. It gets to the line "Pe.BeginPage(PpInfo, PInfo, true, null);"
and then as soon as I step past it I get an error in AutoCAD saying "INTERNAL ERROR: !dbplotset.cpp@422: eLockViolation"
Nobody seems to be able to give me a straight answer as to what this means, or why my code has only minor differences to yours (negligible) and yet when I run them together mine hits that point and crashes while yours doesn't.
Posted by: Andrew | January 22, 2009 at 02:55 AM
A lock violation typically means that access to a document has been attempted without it being locked or a second attempt has been made to access a currently locked document - I forget exactly which.
The current document is locked implicitly when a command is entered, so perhaps you're not plotting the current document and have forgotten to lock it?
Another (less likely) possibility is that it's somehow related to background plotting.
If these suggestions don't help I suggest contacting the ADN team (if you're a member), or post it to the AutoCAD .NET Discussion Group, if not.
Kean
Posted by: Kean Walmsley | January 22, 2009 at 09:42 AM
I figured it was something to do with access to the document. That being said, I thought I had already tested for that by commenting out practically my entire program apart from the plotting code and yet I'm still getting the same error. I then thought it was because the command is being run from a modeless dialog, but figured that wouldn't have any influence on a command run from it.
At the moment my entire program is a foreach statement: foreach(string CurrentDwg in LstDwg.Items)
this encloses: Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.Open(CurrentDwg, false);
and then proceeds straight to your plot code. After this is closes and discards changes.
Posted by: Andrew | January 26, 2009 at 11:34 PM
How is "the command" being run from a modeless dialog? If the code is simply behind a button (or another control), then it's running in the session context, so you must lock documents manually.
The best way to drive functionality from a modeless dialog is to define actual commands and fire them off using SendStringToExecute(). That takes care of locking and lots of other bits and pieces.
Although as you're looping through documents, you'll need to lock them yourself.
Kean
Posted by: Kean Walmsley | January 27, 2009 at 06:23 AM
Once you enter the CommandMethod I've set up it opens the dialog. You select the settings you want to use (drawings, job number, etc.) and press the "Go" button.
The above code is then run.
The only thing I can think of now is closing each drawing, and then opening as read only before the plot code.
I'll look into SendStringToExecute but for my purposes it may not work too well. At least I know where I'm going wrong now. Sort of.
Posted by: Andrew | January 28, 2009 at 06:32 AM
Try using Document.LockDocument() before you plot each one.
SendStringToExecute() is actually a red herring, in this case: you have a session command that is working with multiple documents, so document locking is most likely the answer.
Kean
Posted by: Kean Walmsley | January 28, 2009 at 12:50 PM
WORKED FIRST TIME!
Can't believe the entire problem was missing 3 words!
Posted by: Andrew | January 29, 2009 at 04:36 AM
Hello Kean!
If I run your function in modelspace, all is ok, but if I run your function when a layout with viewport is active i always get the error "eNotCurrentLayout" at "piv.Validate(pi);"!
Is their something which i don't understand or works this function only if the modelspace is active?
thanks in advance
Patrick
Posted by: Patrick Ottenschläger | February 23, 2009 at 03:55 PM
Hi Patrick,
It works fine when a paperspace layout is selected, but I see it does fail if the modelspace viewport within that layout is active.
I haven't coded to check for that (which it would take me some work to do), but there are couple of easy ways to code defensively for this case.
1) You could execute a PSPACE command prior to executing the plot.
2) You could use COM to do the same...
Include the namespace for AutoCAD's COM interop assembly (after adding the assembly reference, of course):
using Autodesk.AutoCAD.Interop;
And this code early on in the SIMPLOT command:
AcadDocument ad =
(AcadDocument)doc.AcadDocument;
ad.MSpace = false;
I hope this helps,
Kean
Posted by: Kean Walmsley | February 24, 2009 at 01:56 PM
I have used your example to some success, with the addition of reading the active layout's canonical media size before setting the new plot configuration.
Having solved that one, I am having difficulty modifying to produce a plot preview. Do you have any pointers?
Posted by: markc | June 01, 2009 at 07:22 PM
This post should help.
Kean
Posted by: Kean Walmsley | June 01, 2009 at 07:28 PM
Thanks for the pointers to the preview code.
I have managed finally to program a custom eTransmit command that includes the plot to PDF and DWF along with a bound drawing.
I was performing some final testing with a particularly large file (70MB) on a standard ISO plot. The plot to PDF can be completed manually, however when I execute the plot using .net I receive a System.AccessViolationException.
Any attempts to avoid this using a Try Catch results in a pure virtual function call.
The error occurs at:
pe.BeginGenerateGraphics(null);
The plot does appear to begin to work...
Any ideas?
Posted by: markc | June 25, 2009 at 11:59 AM
Hi Mark,
Sorry - I have no idea what's happening, although something about it makes it smell like a memory issue. I'd check to see whether the issue is reproducible with a slightly modified scenario:
a) on another system
b) with another, similarly-sized, drawing
c) using a different (perhaps non-PDF) driver
I'd also suggest submitting your question to the ADN team, if you're a member.
Regards,
Kean
Posted by: Kean Walmsley | June 25, 2009 at 03:52 PM
(meaning a, b or c, not a, b and c... :-)
Kean
Posted by: Kean Walmsley | June 25, 2009 at 03:53 PM
Hello Kean, Do you have any sample in RealDWG application? thank you
Posted by: Alberto Benitez | June 29, 2009 at 06:23 PM
Here's a sample, although it's unrelated to this post (you can't plot from RealDWG).
Kean
Posted by: Kean Walmsley | June 30, 2009 at 09:17 AM