Basically what I'm doing is programmatically creating a frame on a document and placing the frame anywhere.
This works as long as I create the frame somewhere on the pasteboard.
(I'm defining the pasteboard as that white area on which the pages are positioned.)
If I place a frame anywhere fully on the pasteboard then things are all okay.
Even if the new frame slightly overlaps the pasteboard then things are all okay.
However, if I create the new frame so that it is entirely not on the pasteboard then it will crash InDesign.
What I need is some way to determine the rectangular bounds of the pasteboard.
This would mean that I could then push the new frames back onto the pasteboard to stop the crash.
However I can't figure out how to get the pasteboard size.
Any ideas on how to get a PMRect of the pasteboard bounds when given a ISpread?
I better answer my own question. My solution is a bit ugly but surprisingly works well.
Here's the method that I wrote that returns a PMRect when passed the ISpread in question.
The idea behind what I do is to drop points along the x&y until they no longer land on a spread (or pasteboard).
PMRect GetPasteboardBoundsFromSpread(InterfacePtr<ISpread> p_spSpread)
{
if (p_spSpread == nil) return kEmptyRect;
// the spread origin should safely be on the pasteboard
PMPoint ptSpreadOrigin(0,0);
::TransformInnerPointToPasteboard(p_spSpread, &ptSpreadOrigin);
PMReal fXMin = ptSpreadOrigin.X();
PMReal fXMax = ptSpreadOrigin.X();
PMReal fYMin = ptSpreadOrigin.Y();
PMReal fYMax = ptSpreadOrigin.Y();
// work with the front document view
InterfacePtr<IControlView> spControlView(Utils<ILayoutUIUtils>()->QueryFrontView());
if (spControlView == nil) return kEmptyRect;
// go left
bool bDone = false;
while (!bDone)
{
fXMin = fXMin - 1.0;
InterfacePtr<ISpread> spSpread(Utils<IPasteboardUtils>()->QuerySpread(spControlView, PBPMPoint(fXMin, ptSpreadOrigin.Y())));
if (spSpread == nil) bDone = true;
}
fXMin += 1; // we've actually dropped off the spread so at this point we've gone one step too far
// go right
bDone = false;
while (!bDone)
{
fXMax = fXMax + 1.0
InterfacePtr<ISpread> spSpread(Utils<IPasteboardUtils>()->QuerySpread(spControlView, PBPMPoint(fXMax, ptSpreadOrigin.Y())));
if (spSpread == nil) bDone = true;
}
fXMax -= 1; // we've actually dropped off the spread so at this point we've gone one step too far
// go up
bDone = false;
while (!bDone)
{
fYMin = fYMin - 1.0;
InterfacePtr<ISpread> spSpread(Utils<IPasteboardUtils>()->QuerySpread(spControlView, PBPMPoint(ptSpreadOrigin.X(), fYMin)));
if (spSpread == nil) bDone = true;
}
fYMin += 1; // we've actually dropped off the spread so at this point we've gone one step too far
// go down
bDone = false;
while (!bDone)
{
fYMax = fYMax + 1.0;
InterfacePtr<ISpread> spSpread(Utils<IPasteboardUtils>()->QuerySpread(spControlView, PBPMPoint(ptSpreadOrigin.X(), fYMax)));
if (spSpread == nil) bDone = true;
}
fYMax -= 1; // we've actually dropped off the spread so at this point we've gone one step too far
return PMRect(fXMin, fYMin, fXMax, fYMax);
}
If we implement what you suggest, then we only get the total bounds of the items on the spread. For example, if you create a new InDesign document using the defaults, you'll have a rather large pasteboard with a single page area on it - the spread bounds in this case is just the page bounds.
Andrew Andrew,
Do you really need the size of the pasteboard?
Do you want to create frames on the page?
I've hacked some code to create a text frame on all pages in a document. Not saying it is perfect and you should add lots of extra checking. Most of this code has been gleened from the sample in the SDK.
It would be nice if the experts pointed out any short commings.
Here is the code, feel fee to pull it apart. ( it did a rough and dirth job for me ).
========================================================
#include "ISpreadList.h"
#include "ISpread.h"
#include "IGeometry.h"
#include "ITransform.h"
#include "TransformUtils.h" // InnerToPasteboard
#include "SplineID.h" // kSplineItemBoss
#include "PreferenceUtils.h"
#include "INewPageItemCmdData.h"
#include "IMultiColumnItemData.h"
UIDRef CreateTextFrame(const UIDRef& parentUIDRef,
const PMRect& boundsInParentCoords,
int32 numberOfColumns,
bool16 verticalFrame,
UIDRef* storyUIDRef)
{
UIDRef result = UIDRef::gNull;
do
{
PMPoint topLeft = boundsInParentCoords.LeftTop();
PMPoint rightBottom = boundsInParentCoords.RightBottom();
PMPointList points(2);
points.push_back(topLeft);
points.push_back(rightBottom);
// Create the command.
InterfacePtr<ICommand> createTextFrameCmd(CmdUtils::CreateCommand(kCreateMultiColumnItemCmdBoss));
ASSERT(createTextFrameCmd);
if (createTextFrameCmd == nil) {
break;
}
// Set up the INewPageItemCmdData data interface.
InterfacePtr<INewPageItemCmdData> newPageItemCmdData(createTextFrameCmd, UseDefaultIID());
ASSERT(newPageItemCmdData);
if (newPageItemCmdData == nil) {
break;
}
// ClassID of frame in which kMultiColumnItemBoss will be created.
const ClassID graphicFrameClassID = kSplineItemBoss;
newPageItemCmdData->Set(parentUIDRef.GetDataBase(), graphicFrameClassID, parentUIDRef.GetUID(), points);
// Set up ICreateMCFrameData.
InterfacePtr<ICreateMCFrameData> createFrameData(createTextFrameCmd, UseDefaultIID());
ASSERT(createFrameData);
if (createFrameData == nil) {
break;
}
// We recommend you don't activate the text editor via kCreateMultiColumnItemCmdBoss.
// Under InDesign 3.0 the default value of ICreateMCFrameData::SetActivateTextEditor
// has changed from kTrue to kFalse because many expensive notifications happen if
// the text editor is activated.
//createFrameData->SetActivateTextEditor(nil) ; // nil to not activate text editor
// Default is that it isn't activated anyway, this call was bogus (irp)
// Specify the frame orientation
ICreateMCFrameData::Orientation orientation = ICreateMCFrameData::kHorizontal;
if (verticalFrame == kTrue) {
orientation = ICreateMCFrameData::kVertical;
}
createFrameData->SetOrientation(orientation);
// Set up IMultiColumnItemData to override defaults.
InterfacePtr<IMultiColumnItemData> multiColumnItemData(createTextFrameCmd, UseDefaultIID());
ASSERT(multiColumnItemData);
if (!multiColumnItemData) {
break;
}
// CAlert::InformationAlert( "Hello ");
// 1037710+
// Under InDesign 4.0 the text frame options such as
// as insets are now parameters passed to kCreateMultiColumnItemCmdBoss,
// the command no longer picks up these defaults from the workspace,
// instead the client code must direct the command at the values to use.
InterfacePtr<ITextOptions> textOptions((ITextOptions*)::QueryPreferences(ITextOptions::kDefaultIID, parentUIDRef));
if (textOptions) {
multiColumnItemData->SetTextFrameOptionsData(textOptio ns);
InterfacePtr<IBaselineFrameGridData> baselineFrameGridData(textOptions, UseDefaultIID());
if (baselineFrameGridData) {
multiColumnItemData->SetBaselineFrameGridData(bas elineFrameGridData);
}
} //1037710+
if (numberOfColumns > 0) {
multiColumnItemData->SetNumberOfColumns(numberOfColumn s);
}
// Process the command.
if (CmdUtils::ProcessCommand(createTextFrameCmd)!=kSuccess) {
ASSERT_FAIL("kCreateMultiColumnItemCmdBoss failed");
break;
}
// The command returns a reference to the graphic frame, normally a kSplineItemBoss,
// with which the underlying kMultiColumnItemBoss and kFrameItemBoss objects are
// associated.
const UIDList& outItemList = createTextFrameCmd->GetItemListReference();
ASSERT(outItemList.Length() > 0);
if (!(outItemList.Length() > 0)) {
break;
}
result = outItemList.GetRef(0);;
ASSERT(result != UIDRef::gNull);
// If the caller requested it by passing in a pointer return a reference
// to the story associated with the text frame.
if (storyUIDRef) {
*storyUIDRef = UIDRef(parentUIDRef.GetDataBase(), createFrameData->GetStory());
}
} while(false);
return result;
}
#include "ILayerUtils.h"
#include "ISpreadLayer.h"
UID GetDefaultLayerUID(IDataBase *db, ISpread *spread)
{
InterfacePtr<IDocument> doc(db, db->GetRootUID(), UseDefaultIID());
InterfacePtr<IDocumentLayer> activeDocLayer(Utils<ILayerUtils>()->QueryDocumentActiveLayer(doc));
InterfacePtr<ISpreadLayer> spreadLayer(spread->QueryLayer(activeDocLayer));
return ::GetUID(spreadLayer);
}
void do_create_stuff ( void )
{
IDocument *doc = Utils<ILayoutUIUtils>()->GetFrontDocument ( );
if ( !doc )
return;
do {
IDataBase* db = ::GetDataBase( doc );
InterfacePtr<ISpreadList> spreadList ( doc, IID_ISPREADLIST );
if (spreadList)
{
uint32 spreadCount = spreadList->GetSpreadCount ( );
for (uint32 i = 0; i < spreadCount; i++)
{
UID spreadUID = spreadList->GetNthSpreadUID ( i );
InterfacePtr<ISpread> spread(db, spreadUID, UseDefaultIID ( ));
if ( spread )
{
int32 numPages = spread->GetNumPages ( );
for ( int32 j = 0; j < numPages; j++ )
{
UID pageUID = spread->GetNthPageUID ( j );
UID layerUID = GetDefaultLayerUID ( db, spread );
PMRect r(1.0,1.0,20.0,30.0);
InterfacePtr<IGeometry> pageGeometry ( db, pageUID, UseDefaultIID ( ));
::TransformInnerRectToPasteboard ( pageGeometry, &r );
CreateTextFrame ( UIDRef(db, layerUID), r, 1, false, 0L );
}
}
}
}
} while ( false );
}
===============================
P.
@mfreitag "and what about the pasteboard border values from IPasteboardPrefs added to the page bounds?"
This value is a global value that's aggregated to either the workspace boss or the document workspace boss. I can't see how to use it to get from a particular spread within a document to the pasteboard. Remember, the pasteboard for a document is not fixed for every spread in the document, which mean a single document level value is not enough to describe all possible pasteboards in a document.
Also, for me these values are always (-1,72) regardless of how big the pasteboard is with respect to the page. I can't see the connection between a page size and the pasteboard size from this.
Hi Another Andrew,
what do you mean with "all possible pasteboards in a document"? There is only one pasteboard, see programming guide:
===========================
...
Pasteboard coordinate space
The pasteboard coordinate space is the global coordinate system that encloses all objects in a document,
as shown in the following figure.
...
All objects in a layout can have their coordinates expressed in pasteboard coordinates. The origin is the
center of the first spread. The x axis increases from left to right; the y axis decreases from up to down. The
length along each axis is measured in the PostScript unit of points.
...
===========================
To get size of each spread I use the following code and it works as expected:
===========================
IDocument* doc = ac->GetContextDocument();
if (doc)
{
InterfacePtr<ISpreadList> sl(doc, UseDefaultIID());
if (sl)
{
for (int32 i = 0; i < sl->GetSpreadCount(); ++i)
{
InterfacePtr<IGeometry> geo(sl->QueryNthSpread(i));
if (geo)
{
PMRect rect = geo->GetPathBoundingBox();
TransformInnerRectToPasteboard(geo, &rect);
rect = rect;
}
}
}
}
===========================
Markus
North America
Europe, Middle East and Africa
Asia Pacific