Kean专题(12)—Plotting
本帖最后由 作者 于 2009-6-20 17:02:23 编辑http://through-the-interface.typepad.com/through_the_interface/plotting/
一、使用Design Review 2008的批打印插件
May 10, 2007
Automating batch printing of DWF files using Design Review 2008
We sometimes receive questions on how best to automate the printing of DWF files. Autodesk Design Review 2008 now has a new Batch Print Plug-in enabling just this.
Once you've installed the plug-in, you'll be able to use the Batch Print Wizard in Design Review (on the file menu, once a DWF has been loaded). This Wizard allows you to configure a batch job for Design Review to process and save it to a BPJ (Batch Print Job) file. This BPJ (which is basically a very simple XML file) can then be used to drive the batch print process automatically.
The next logical step is clearly to create the BPJ programmatically, which can be done using a number of XML libraries (MS XML etc.) or simply by writing out a text file using your favourite string output routines.
So let's look at creating the BPJ file "manually" using the Batch Print Wizard.
Here's the second screen you'll see, once you've skipped the welcome:
When you've selected the files you wish to print and added them to the right-hand side using the ">" button, you can move on to the next page:
This is where you modify the print setup for each of your DWFs, by clicking on the item and hitting "Print Setup", or just double-clicking the item.
And then you're ready to save the BPJ file (or just "Print" directly).
I saved mine as "c:\My Documents\MyFiles.bpj", and here are its contents, with additional whitespace added for readability:<configuration_file>
<DWF_File
FileName="C:\My Documents\\First.dwf"
PageSize="11.0 x 8.5 in"
NoOfSections="2"
Print_to_scale="100"
User_defined_Zoom_Factor="100"
Print_Style="0"
Print_What="0"
Fit_To_Paper="1"
Paper_Size="9"
Paper_Size_Width="2100"
Paper_Size_Height="2970"
Orientation="2"
Number_of_copies="1"
PrinterName="\\beihpa001\BEIPRN003"
Page_Range="1"
Print_Range_Str=""
Reverse_Order="0"
Collate="0"
printColor="0"
printAlignment="4"
Use_DWF_Paper_Size="0"
PaperName="A4"
useHPIP="0"
HPIPMediaID="-1"
HPIPExcludeEModel="1"
HPIPPaperName=""/>
<DWF_File
FileName="C:\My Documents\\Second.dwf"
PageSize="11.0 x 8.5 in"
NoOfSections="2"
Print_to_scale="100"
User_defined_Zoom_Factor="100"
Print_Style="0"
Print_What="0"
Fit_To_Paper="1"
Paper_Size="9"
Paper_Size_Width="2100"
Paper_Size_Height="2970"
Orientation="2"
Number_of_copies="1"
PrinterName="\\beihpa001\BEIPRN003"
Page_Range="1"
Print_Range_Str=""
Reverse_Order="0"
Collate="0"
printColor="0"
printAlignment="4"
Use_DWF_Paper_Size="0"
PaperName="A4"
useHPIP="0"
HPIPMediaID="-1"
HPIPExcludeEModel="1"
HPIPPaperName=""/>
</configuration_file>
This is clearly straightforward to create programmatically from your application. The next question is how best to automate the print process, now we have our BPJ.
The simplest way is to call the Design Review executable with the BPJ file as a command-line argument:
The executable returns straight away, and you'll see a log file created in the same location as the BPJ (in my case "c:\My Documents\MyFiles.log"), detailing the results of the print job:Batch Printing initiated with Configuration file: "\My Documents\MyFiles.bpj". Start Time: Thu May 10 17:16:35 2007
Opening DWF file : "C:\My Documents\\First.dwf". Start Time: Thu May 10 17:16:35 2007
DWF file: "C:\My Documents\\First.dwf" Opened successfully. End Time: Thu May 10 17:16:37 2007
Status:DWF Open Successful
Started Printing DWF File: "C:\My Documents\\First.dwf". Start Time: Thu May 10 17:16:39 2007
Printing To : \\beihpa001\BEIPRN003
HPIP set to : False
Paper Size : 2100 X 2970
Print Range : Current Page
No Of Copies: 1
Scale : 100 %, Tile Pages (Not Applicable For 3D Sections)
Print Alignment: LowerLeft (Not Applicable For 3D Sections)
Orientation : Landscape
Print Color : Color
Started Printing Section Number : 0. Start Time: Thu May 10 17:16:39 2007
Finished Printing Section Number: 0. End Time: Thu May 10 17:16:42 2007
Started Printing Section Number : 1. Start Time: Thu May 10 17:16:43 2007
Finished Printing Section Number: 1. End Time: Thu May 10 17:16:43 2007
Finished Printing DWF file: "C:\My Documents\\First.dwf" . End Time: Thu May 10 17:16:43 2007
Opening DWF file : "C:\My Documents\\Second.dwf". Start Time: Thu May 10 17:16:43 2007
DWF file: "C:\My Documents\\Second.dwf" Opened successfully. End Time: Thu May 10 17:16:45 2007
Status:DWF Open Successful
Started Printing DWF File: "C:\My Documents\\Second.dwf". Start Time: Thu May 10 17:16:46 2007
Printing To : \\beihpa001\BEIPRN003
HPIP set to : False
Paper Size : 2100 X 2970
Print Range : Current Page
No Of Copies: 1
Scale : 100 %, Tile Pages (Not Applicable For 3D Sections)
Print Alignment: LowerLeft (Not Applicable For 3D Sections)
Orientation : Landscape
Print Color : Color
Started Printing Section Number : 0. Start Time: Thu May 10 17:16:46 2007
Finished Printing Section Number: 0. End Time: Thu May 10 17:16:49 2007
Started Printing Section Number : 1. Start Time: Thu May 10 17:16:49 2007
Finished Printing Section Number: 1. End Time: Thu May 10 17:16:50 2007
Finished Printing DWF file: "C:\My Documents\\Second.dwf" . End Time: Thu May 10 17:16:50 2007
Batch Printing Finished with Configuration file: "\My Documents\MyFiles.bpj". End Time: Thu May 10 17:16:50 2007
Total Time Taken to complete Batch Print Process: 0 hr: 0 Min: 15 Sec.
This log file can be parsed programmatically if it's important for your application to know whether any pages had difficulty printing.
图纸尺寸列表中 显示的是英文 MM,而不是中文 毫米,,而CAD自带德打印模块显示的 图纸尺寸 是 中文 毫米,编程 怎么实现
打印预览有没有调过了的?为啥一直卡在预览界面? 打印预览有没有调过了的?为啥一直卡在预览界面? 二、使用NetApi实现简单的打印任务
September 25, 2007
Driving a basic AutoCAD plot using .NET
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
{
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):
三、使用NetApi实现多表打印任务
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
{
public class PlottingCommands
{
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:
四、单表打印预览
October 01, 2007
Previewing and plotting a single sheet in AutoCAD using .NET
This week's posts take the code I threw together last week for single-sheet and multi-sheet plotting, and introduces the concept of "plot preview".
I'm learning as I go for much of this, so there are structural (although usually not functional) changes being made to the code as it develops. In this instance, for example, I've factored off common functionality needed by both previewing and plotting into a single helper function. This will no doubt evolve further (and change in structure) when I come to apply the principle to multi-sheet plotting later in the week.
Here's the C# code:
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.PlottingServices;
namespace PlottingApplication
{
public class PreviewCommands
{
static public void SimplePreview()
{
Document doc =
Application.DocumentManager.MdiActiveDocument;
Editor ed = doc.Editor;
Database db = doc.Database;
// PlotEngines do the previewing and plotting
if (PlotFactory.ProcessPlotState ==
ProcessPlotState.NotPlotting)
{
// First we preview...
PreviewEndPlotStatus stat;
PlotEngine pre =
PlotFactory.CreatePreviewEngine(
(int)PreviewEngineFlags.Plot
);
using (pre)
{
stat =
PlotOrPreview(
pre,
true,
db.CurrentSpaceId,
""
);
}
if (stat == PreviewEndPlotStatus.Plot)
{
// And if the user asks, we plot...
PlotEngine ple =
PlotFactory.CreatePublishEngine();
stat =
PlotOrPreview(
ple,
false,
db.CurrentSpaceId,
"c:\\previewed-plot"
);
}
}
else
{
ed.WriteMessage(
"\nAnother plot is in progress."
);
}
}
static PreviewEndPlotStatus PlotOrPreview(
PlotEngine pe,
bool isPreview,
ObjectId spaceId,
string filename)
{
Document doc =
Application.DocumentManager.MdiActiveDocument;
Editor ed = doc.Editor;
Database db = doc.Database;
PreviewEndPlotStatus ret =
PreviewEndPlotStatus.Cancel;
Transaction tr =
db.TransactionManager.StartTransaction();
using (tr)
{
// We'll be plotting the current layout
BlockTableRecord btr =
(BlockTableRecord)tr.GetObject(
spaceId,
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);
// Create a Progress Dialog to provide info
// and allow thej user to cancel
PlotProgressDialog ppd =
new PlotProgressDialog(isPreview, 1, true);
using (ppd)
{
ppd.set_PlotMsgString(
PlotMessageIndex.DialogTitle,
"Custom Preview Progress"
);
ppd.set_PlotMsgString(
PlotMessageIndex.SheetName,
doc.Name.Substring(
doc.Name.LastIndexOf("\\") + 1
)
);
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/preview, at last
ppd.OnBeginPlot();
ppd.IsVisible = true;
pe.BeginPlot(ppd, null);
// We'll be plotting/previewing
// a single document
pe.BeginDocument(
pi,
doc.Name,
null,
1,
!isPreview,
filename
);
// 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);
ppd.SheetProgressPos = 50;
pe.EndGenerateGraphics(null);
// Finish the sheet
PreviewEndPlotInfo pepi =
new PreviewEndPlotInfo();
pe.EndPage(pepi);
ret = pepi.Status;
ppd.SheetProgressPos = 100;
ppd.OnEndSheet();
// Finish the document
pe.EndDocument(null);
// And finish the plot
ppd.PlotProgressPos = 100;
ppd.OnEndPlot();
pe.EndPlot(null);
}
// Committing is cheaper than aborting
tr.Commit();
}
return ret;
}
}
}
When you execute the SIMPREV command, you receive enter a "preview" mode, from where you can either cancel or plot. The trick was really in determining the button selected by the user, which we do by passing an appropriate object (of class PreviewEndPlotInfo) into the EndPage() function, to collect information on what the users selects. The next post will take this further, allowing the user to cycle through multiple sheets using "next" and "previous" buttons.
五、多表打印预览
October 04, 2007
Previewing and plotting multiple sheets in AutoCAD using .NET
This was a fun one to work on. The code in this post combines and extends upon techniques shown in two earlier posts: one showing how to plot multiple sheets and the other showing how to preview a single-sheet plot.
One of the key differences when plotting or previewing is that while plotting can directly support multiple sheets (assuming the device does so), previewing does not. The good news is that AutoCAD provides you the user interface elements to allow cycling through plots: the user is provided with "Next" and "Previous" buttons - it's then up to you to implement the appropriate logic to preview different sheets when the buttons are used.
I chose to use the same helper function for both preview and plot, even though they are a little different in nature. The reason is obvious (to me, at least) - it reduces the amount of code to debug and maintain - but it might, for some, make the code a little less easy to read. Ultimately the trick I used was to reduce the set of sheets being handled at the beginning of the function to a single sheet in the case of a preview, which allowed me to combine both approaches in a single function.
Here's the C# code:
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.PlottingServices;
using System.Collections.Generic;
namespace PlottingApplication
{
public class PreviewCommands
{
static public void MultiSheetPreview()
{
Document doc =
Application.DocumentManager.MdiActiveDocument;
Editor ed = doc.Editor;
Database db = doc.Database;
ObjectIdCollection layoutsToPlot =
new ObjectIdCollection();
Transaction tr =
db.TransactionManager.StartTransaction();
using (tr)
{
// First we need to collect the layouts to
// plot/preview in tab order
SortedDictionary<int, ObjectId> layoutDict =
new SortedDictionary<int, ObjectId>();
BlockTable bt =
(BlockTable)tr.GetObject(
db.BlockTableId,
OpenMode.ForRead
);
foreach (ObjectId btrId in bt)
{
BlockTableRecord btr =
(BlockTableRecord)tr.GetObject(
btrId,
OpenMode.ForRead
);
if (btr.IsLayout &&
btr.Name.ToUpper() !=
BlockTableRecord.ModelSpace.ToUpper())
{
// The dictionary we're using will
// sort on the tab order of the layout
Layout lo =
(Layout)tr.GetObject(
btr.LayoutId,
OpenMode.ForRead
);
layoutDict.Add(lo.TabOrder,btrId);
}
}
// Let's now get the layout IDs and add them to a
// standard ObjectIdCollection
SortedDictionary<int,ObjectId>.ValueCollection vc =
layoutDict.Values;
foreach (ObjectId id in vc)
{
layoutsToPlot.Add(id);
}
// Committing is cheaper than aborting
tr.Commit();
}
// PlotEngines do the previewing and plotting
if (PlotFactory.ProcessPlotState ==
ProcessPlotState.NotPlotting)
{
int layoutNum = 0;
bool isFinished = false;
bool isReadyForPlot = false;
while (!isFinished)
{
// Create the preview engine with the appropriate
// buttons enabled - this depends on which
// layout in the list is being previewed
PreviewEngineFlags flags =
PreviewEngineFlags.Plot;
if (layoutNum > 0)
flags |= PreviewEngineFlags.PreviousSheet;
if (layoutNum < layoutsToPlot.Count - 1)
flags |= PreviewEngineFlags.NextSheet;
PlotEngine pre =
PlotFactory.CreatePreviewEngine((int)flags);
using (pre)
{
PreviewEndPlotStatus stat =
MultiplePlotOrPreview(
pre,
true,
layoutsToPlot,
layoutNum,
""
);
// We're not checking the list bounds for
// next/previous as the buttons are only shown
// when they can be used
if (stat == PreviewEndPlotStatus.Next)
{
layoutNum++;
}
else if (stat == PreviewEndPlotStatus.Previous)
{
layoutNum--;
}
else if (stat == PreviewEndPlotStatus.Normal ||
stat == PreviewEndPlotStatus.Cancel)
{
isFinished = true;
}
else if (stat == PreviewEndPlotStatus.Plot)
{
isFinished = true;
isReadyForPlot = true;
}
}
}
// If the plot button was used to exit the preview...
if (isReadyForPlot)
{
PlotEngine ple =
PlotFactory.CreatePublishEngine();
using (ple)
{
PreviewEndPlotStatus stat =
MultiplePlotOrPreview(
ple,
false,
layoutsToPlot,
-1,
"c:\\multisheet-previewed-plot"
);
}
}
}
else
{
ed.WriteMessage(
"\nAnother plot is in progress."
);
}
}
static PreviewEndPlotStatus MultiplePlotOrPreview(
PlotEngine pe,
bool isPreview,
ObjectIdCollection layoutSet,
int layoutNumIfPreview,
string filename
)
{
Document doc =
Application.DocumentManager.MdiActiveDocument;
Editor ed = doc.Editor;
Database db = doc.Database;
PreviewEndPlotStatus ret =
PreviewEndPlotStatus.Cancel;
ObjectIdCollection layoutsToPlot;
if (isPreview && layoutNumIfPreview >= 0)
{
// Preview is really pre-sheet, so we reduce the
// sheet collection to contain the one we want
layoutsToPlot = new ObjectIdCollection();
layoutsToPlot.Add(
layoutSet
);
}
else
{
// If we're plotting we need all the sheets,
// so copy the ObjectIds across
ObjectId[] ids = new ObjectId;
layoutSet.CopyTo(ids,0);
layoutsToPlot = new ObjectIdCollection(ids);
}
Transaction tr =
db.TransactionManager.StartTransaction();
using (tr)
{
// Create a Progress Dialog to provide info
// and allow thej user to cancel
PlotProgressDialog ppd =
new PlotProgressDialog(
isPreview,
layoutsToPlot.Count,
true
);
using (ppd)
{
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
PlotInfo pi = new PlotInfo();
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;
PlotInfoValidator piv =
new PlotInfoValidator();
piv.MediaMatchingPolicy =
MatchingPolicy.MatchEnabled;
piv.Validate(pi);
// We set the sheet name per sheet
ppd.set_PlotMsgString(
PlotMessageIndex.SheetName,
doc.Name.Substring(
doc.Name.LastIndexOf("\\") + 1
) +
" - " +
lo.LayoutName
);
if (numSheet == 1)
{
// All other messages get set once
ppd.set_PlotMsgString(
PlotMessageIndex.DialogTitle,
"Custom Preview 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/preview, 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,
!isPreview,
filename
);
}
// Which may contains multiple sheets
ppd.LowerSheetProgressRange = 0;
ppd.UpperSheetProgressRange = 100;
ppd.SheetProgressPos = 0;
PlotPageInfo ppi = new PlotPageInfo();
pe.BeginPage(
ppi,
pi,
(numSheet == layoutsToPlot.Count),
null
);
ppd.OnBeginSheet();
pe.BeginGenerateGraphics(null);
ppd.SheetProgressPos = 50;
pe.EndGenerateGraphics(null);
// Finish the sheet
PreviewEndPlotInfo pepi =
new PreviewEndPlotInfo();
pe.EndPage(pepi);
ret = pepi.Status;
ppd.SheetProgressPos = 100;
ppd.OnEndSheet();
numSheet++;
// Update the overall progress
ppd.PlotProgressPos +=
(100 / layoutsToPlot.Count);
}
// Finish the document
pe.EndDocument(null);
// And finish the plot
ppd.PlotProgressPos = 100;
ppd.OnEndPlot();
pe.EndPlot(null);
}
}
return ret;
}
}
}
Here's what you see when you run the MPREV command:
Once you select the plot button, the plot gets driven as before:
六、选择打印设备和打印介质
October 09, 2007
Allowing selection of an AutoCAD plot device and media name using .NET
A comment came in on this previous post regarding how best to know whether a media name is valid during your plot configuration.
There are a few approaches, other than the one I chose of hardcoding the device and media names. The first is to implement a user interface of some kind which allows the user to select the device and media names. Another approach for setting the media name is to use PlotSettingsValidator.SetClosestMediaName() to choose the media name that most closely matches the paper size you desire.
Today I'll focus on the first option, although I'm only going to implement a basic, command-line user interface. It should be straightforward to extend this to implement a WinForm with comboboxes for the device and media names.
I implemented a simple function called ChooseDeviceAndMedia() which queries the current device list, allows selection from it, and then gets the media name list and allows selection from that. I chose not to worry about displaying locale-specific names, for now - I'll leave that for a future post (let me know if you're interested :-).
Here's the function integrated into the C# code from this previous post, which defines a command called MPLOT:
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.PlottingServices;
using System.Collections.Specialized;
namespace PlottingApplication
{
public class PlottingCommands
{
static public string[] ChooseDeviceAndMedia()
{
Document doc =
Application.DocumentManager.MdiActiveDocument;
Editor ed = doc.Editor;
// Assign default return values
string devname = "", medname = "";
PlotSettingsValidator psv =
PlotSettingsValidator.Current;
// Let's first select the device
StringCollection devlist =
psv.GetPlotDeviceList();
for (int i = 0; i < devlist.Count; i++)
{
ed.WriteMessage(
"\n{0} {1}",
i + 1,
devlist
);
}
PromptIntegerOptions opts =
new PromptIntegerOptions(
"\nEnter number of device to select: "
);
opts.LowerLimit = 1;
opts.UpperLimit = devlist.Count;
PromptIntegerResult pir =
ed.GetInteger(opts);
if (pir.Status == PromptStatus.OK)
{
devname = devlist;
ed.WriteMessage(
"\nSelected: {0}\n",
devname
);
// Now let's select the media
PlotSettings ps = new PlotSettings(true);
using (ps)
{
// We should refresh the lists,
// in case setting the device impacts
// the available media
psv.SetPlotConfigurationName(
ps,
devname,
null
);
psv.RefreshLists(ps);
StringCollection medlist =
psv.GetCanonicalMediaNameList(ps);
for (int i = 0; i < medlist.Count; i++)
{
ed.WriteMessage(
"\n{0} {1}",
i + 1,
medlist
);
}
opts.Message =
"\nEnter number of media to select: ";
opts.LowerLimit = 1;
opts.UpperLimit = medlist.Count;
pir = ed.GetInteger(opts);
if (pir.Status == PromptStatus.OK)
{
medname = medlist;
ed.WriteMessage(
"\nSelected: {0}\n",
medname
);
}
}
}
return new string { devname, medname };
}
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)
{
string[] devmed = ChooseDeviceAndMedia();
// Only proceed if we have values for both
if (devmed != "" && devmed != "")
{
string devname = devmed;
string medname = devmed;
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)
{
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,
devname,
medname
);
// 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 contains multiple sheets
ppd.set_PlotMsgString(
PlotMessageIndex.SheetName,
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++;
ppd.PlotProgressPos +=
(100 / layoutsToPlot.Count);
}
// 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."
);
}
}
}
}
}
Here's what I see at the command-line when I run the MPLOT command on my system (items 7-18 below are network printers in various Autodesk offices):
Command: MPLOT
1 None
2 SnagIt 8
3 PDF-XChange 3.0
4 Microsoft XPS Document Writer
5 Microsoft Office Live Meeting Document Writer
6 Adobe PDF
7 \\adscctps1\cctprn000
8 \\adsneups2\neuprn304
9 \\adsneups2\neuprn306
10 \\adsneups2\neuprn307
11 \\adsneups2\neuprn317
12 \\banhpa001\banprn001
13 \\beihpa001\BEIPRN002
14 \\beihpa001\BEIPRN003
15 \\farhpa001\FARPRN101
16 \\pra-pc61237\Xerox WorkCentre Pro 35 PCL6
17 \\tokhpa001\tokprn401
18 \\tokhpa001\tokprn402
19 Default Windows System Printer.pc3
20 DWF6 ePlot.pc3
21 DWFx ePlot (XPS Compatible).pc3
22 DWG To PDF.pc3
23 PublishToWeb JPG.pc3
24 PublishToWeb PNG.pc3
Enter number of device to select: 22
Selected: DWG To PDF.pc3
1 ISO_expand_A0_(841.00_x_1189.00_MM)
2 ISO_A0_(841.00_x_1189.00_MM)
3 ISO_expand_A1_(841.00_x_594.00_MM)
4 ISO_expand_A1_(594.00_x_841.00_MM)
5 ISO_A1_(841.00_x_594.00_MM)
6 ISO_A1_(594.00_x_841.00_MM)
7 ISO_expand_A2_(594.00_x_420.00_MM)
8 ISO_expand_A2_(420.00_x_594.00_MM)
9 ISO_A2_(594.00_x_420.00_MM)
10 ISO_A2_(420.00_x_594.00_MM)
11 ISO_expand_A3_(420.00_x_297.00_MM)
12 ISO_expand_A3_(297.00_x_420.00_MM)
13 ISO_A3_(420.00_x_297.00_MM)
14 ISO_A3_(297.00_x_420.00_MM)
15 ISO_expand_A4_(297.00_x_210.00_MM)
16 ISO_expand_A4_(210.00_x_297.00_MM)
17 ISO_A4_(297.00_x_210.00_MM)
18 ISO_A4_(210.00_x_297.00_MM)
19 ARCH_expand_E1_(30.00_x_42.00_Inches)
20 ARCH_E1_(30.00_x_42.00_Inches)
21 ARCH_expand_E_(36.00_x_48.00_Inches)
22 ARCH_E_(36.00_x_48.00_Inches)
23 ARCH_expand_D_(36.00_x_24.00_Inches)
24 ARCH_expand_D_(24.00_x_36.00_Inches)
25 ARCH_D_(36.00_x_24.00_Inches)
26 ARCH_D_(24.00_x_36.00_Inches)
27 ARCH_expand_C_(24.00_x_18.00_Inches)
28 ARCH_expand_C_(18.00_x_24.00_Inches)
29 ARCH_C_(24.00_x_18.00_Inches)
30 ARCH_C_(18.00_x_24.00_Inches)
31 ANSI_expand_E_(34.00_x_44.00_Inches)
32 ANSI_E_(34.00_x_44.00_Inches)
33 ANSI_expand_D_(34.00_x_22.00_Inches)
34 ANSI_expand_D_(22.00_x_34.00_Inches)
35 ANSI_D_(34.00_x_22.00_Inches)
36 ANSI_D_(22.00_x_34.00_Inches)
37 ANSI_expand_C_(22.00_x_17.00_Inches)
38 ANSI_expand_C_(17.00_x_22.00_Inches)
39 ANSI_C_(22.00_x_17.00_Inches)
40 ANSI_C_(17.00_x_22.00_Inches)
41 ANSI_expand_B_(17.00_x_11.00_Inches)
42 ANSI_expand_B_(11.00_x_17.00_Inches)
43 ANSI_B_(17.00_x_11.00_Inches)
44 ANSI_B_(11.00_x_17.00_Inches)
45 ANSI_expand_A_(11.00_x_8.50_Inches)
46 ANSI_expand_A_(8.50_x_11.00_Inches)
47 ANSI_A_(11.00_x_8.50_Inches)
48 ANSI_A_(8.50_x_11.00_Inches)
Enter number of media to select: 35
Selected: ANSI_D_(34.00_x_22.00_Inches)
Effective plotting area:15.29 wide by 20.60 high
Effective plotting area:13.29 wide by 17.31 high
Plotting viewport 2.
Plotting viewport 1.
Regenerating layout.
Regenerating model.
Effective plotting area:15.69 wide by 20.60 high
Effective plotting area:15.69 wide by 20.59 high
Plotting viewport 2.
Plotting viewport 1.
Regenerating layout.
Regenerating model.
Effective plotting area:15.69 wide by 20.60 high
Effective plotting area:15.69 wide by 20.59 high
Plotting viewport 2.
Plotting viewport 1.
Regenerating layout.
Regenerating model.
Effective plotting area:15.69 wide by 20.60 high
Effective plotting area:14.07 wide by 18.34 high
Plotting viewport 2.
Plotting viewport 1.
Regenerating layout.
Regenerating model.
七、窗选打印
October 11, 2007
Plotting a window from AutoCAD using .NET
This post extends this previous post that dealt with driving a single-sheet AutoCAD plot by adding some code to handle selection and transformation of a window to plot.
First order of business was to allow the user to select the window to plot. For this I used the classic combination of Editor.GetPoint() for the first corner) and Editor.GetCorner() for the second. All well and good, but the points returned by these functions are in UCS (User Coordinate System) coordinates. Which meant that as it stood, the code would work just fine if (and only if) the view we were using to select the window was parallell with the World Coordinate System (and no additional UCS was in place). In order to deal with the very common scenario of either a UCS being used or the current modelspace view being orbited (etc.), we need to transform the current UCS coordinates into DCS, the Display Coordinate System.
Thankfully that's pretty easy: no need for matrices or anything, we simply use another old friend, acedTrans() (the ObjectARX equivalent of the (trans) function, for you LISPers out there). There isn't a direct equivalent for this function in the managed API (that I'm aware of), so we have to use P/Invoke to call the ObjectARX function. The technique is shown in this DevNote, for those of you that have access to the ADN website:
How to call acedTrans from .NET application?
Otherwise the changes to the original code are very minor: we need to set the extents of the window to plot and then set the plot type to "window", apparently in that order. AutoCAD complained when I initially made the calls in the opposite sequence.
Here's the C# code:
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.PlottingServices;
using Autodesk.AutoCAD.Geometry;
using System.Runtime.InteropServices;
using System;
namespace PlottingApplication
{
public class SimplePlottingCommands
{
[DllImport("acad.exe",
CallingConvention = CallingConvention.Cdecl,
EntryPoint="acedTrans")
]
static extern int acedTrans(
double[] point,
IntPtr fromRb,
IntPtr toRb,
int disp,
double[] result
);
static public void WindowPlot()
{
Document doc =
Application.DocumentManager.MdiActiveDocument;
Editor ed = doc.Editor;
Database db = doc.Database;
PromptPointOptions ppo =
new PromptPointOptions(
"\nSelect first corner of plot area: "
);
ppo.AllowNone = false;
PromptPointResult ppr =
ed.GetPoint(ppo);
if (ppr.Status != PromptStatus.OK)
return;
Point3d first = ppr.Value;
PromptCornerOptions pco =
new PromptCornerOptions(
"\nSelect second corner of plot area: ",
first
);
ppr = ed.GetCorner(pco);
if (ppr.Status != PromptStatus.OK)
return;
Point3d second = ppr.Value;
// Transform from UCS to DCS
ResultBuffer rbFrom =
new ResultBuffer(new TypedValue(5003, 1)),
rbTo =
new ResultBuffer(new TypedValue(5003, 2));
double[] firres = new double[] { 0, 0, 0 };
double[] secres = new double[] { 0, 0, 0 };
// Transform the first point...
acedTrans(
first.ToArray(),
rbFrom.UnmanagedObject,
rbTo.UnmanagedObject,
0,
firres
);
// ... and the second
acedTrans(
second.ToArray(),
rbFrom.UnmanagedObject,
rbTo.UnmanagedObject,
0,
secres
);
// We can safely drop the Z-coord at this stage
Extents2d window =
new Extents2d(
firres,
firres,
secres,
secres
);
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.SetPlotWindowArea(ps, window);
psv.SetPlotType(
ps,
Autodesk.AutoCAD.DatabaseServices.PlotType.Window
);
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."
);
}
}
}
}
}
有翻译过来的么,看着眼晕 有自定义图幅的方法吗? 打印预览有没有调过了的?为啥一直卡在预览界面?
页:
[1]
2