Copy link to clipboard
Copied
Okay, So when you use a clipping mask there can sometimes be a lot of art that is invisible. If you "Select All" you'll get a rectangle only around what's visible. I want the bounds of that rectangle.
I tried itterating the art but I don't think that the art type is the best way to figure this one out. And if you use AIGroupSuite to test if an art group is "clipped" with GetGroupClipped, you get the bounds of the larger art that I'm trying to avoid.
I didn't see anything about bounds of a "selection" in the docs.
Anyone got a hint on where to look for this one?
Copy link to clipboard
Copied
What you could do is:
1- to get all selected arts. (use SDK api to get selection or do a recursive treewalk).
2- parse selected arts, and for each art, check art property (visible).
3- and then compute selection bounds if art is visible.
-> this way you will get selection bounds.
Thomas.
Copy link to clipboard
Copied
It is #3 that I'm unsure about. I couldn't find "selection bounds" anywhere in the AIArt suite. Is there a specific suite I should be poking around in?
Copy link to clipboard
Copied
you should do this way:
//Get Art Bounds for each AIArtHandle
AIRealRect artBounds;
error = sAIArt->GetArtBounds(*iter, &artBounds);
aisdk::check_ai_error(error);
//Compute selection bouding box.
AIRealRect& boundingBox;
//for each selected AIArtHandle, bouding box needs to be updated. (loop)
if(boundingBox.left > artBounds.left)
boundingBox.left = artBounds.left;
if(boundingBox.top < artBounds.top)
boundingBox.top = artBounds.top;
if(boundingBox.right < artBounds.right)
boundingBox.right = artBounds.right;
if(boundingBox.bottom > artBounds.bottom)
boundingBox.bottom = artBounds.bottom;
Finally, you will have your selection bounding box.
Copy link to clipboard
Copied
Oh yes, I see what you mean. I thought you meant that there was a specific method for getting selection bounds. I have similar code to what you posted for getting the bounding box of some other art. But the problem is with masks.
Even though the visible selected art shows a smaller bounding rectangle in Illustrator, when one itterates through the art to get the bounds, one receives the entire bounds of each visible art object weather or not all of that art is masked.
For example: The first image shows the path of the art that is masked by the green square. If you itterate the art you will get the bounding box that includes the path of the green square PLUS the path of the line. The second image shows what you get when you do a "select all". I want to get the bounds of the selection in the second image.
I know the bounds I'm looking for must exist somewhere because Illustrator draws the proper box every time "select all" is performed. I just don't know if those bounds are accessable to me and I haven't found a "work around" to compute them yet.
Copy link to clipboard
Copied
1- first thing that you have to do is to check object type.
2- if AIArtHandle is kGroupArt, you need to check the clipped property.
//Check object type:
if((objectType == kGroupArt))
{
error = sAIGroup->GetGroupClipped(*iter, &isClippingGroup);
aisdk::check_ai_error(error);
}
3- And if is the case, you have to get the bounding box of the clipping path.
here is the trick.
AIBoolean isClippingGroup = false;
AIRealRect artBounds;
if(isClippingGroup)
{
// get clipping art bounds instead
AIArtHandle child;
error = sAIArt->GetArtFirstChild(*iter, &child);
aisdk::check_ai_error(error);
error = sAIArt->GetArtBounds(child, &artBounds);
aisdk::check_ai_error(error);
}
This should work!
Thomas.
Copy link to clipboard
Copied
Ahhh, I see. Previously I was getting using GetGroupClipped, but was then getting the bounds of the "clipped" layer. I was not aware that the "first child" would be the clipping bounds. Will that always be true?
Copy link to clipboard
Copied
When a mask is created by Adobe Illustrator, the clipping group’s first child
is the clipping path.
Copy link to clipboard
Copied
Okay... Sadly that is not true. It is possible to create a situation where the clipping group's first child is not the clipping path.
As you can see here the pink elipse is above the clipping path in the layers window AND that is also true of the art objects when I step through the code:
But, like I said, if illustrator can compute the bounding box that I need when "select all" is performed, why shouldn't I be able to as well?
Copy link to clipboard
Copied
To determine which object in the group is a mask, use the function GetArtUserAttr and check by kArtIsClipMask attribute
Copy link to clipboard
Copied
Yes, thank you. I should have reported that I'm now aware of several ways to get the clipping mask.
We know how to tell if an art object is "clipping group" (AIGroupSuite::GetGroupClipped). And I believe that AIArtSuite::IsArtClipping tells if the art object is a "clipping path". The documentation says that AIArtSuite::IsArtClipping basically checks for the kArtIsClipMask attribute that you speak of.
However without a way to tell which items are the ones that are being partially hidden by the clipping mask, I am unable to weed out those objects' bounds as part of my computation to get the box of only visibly displayed material.
Copy link to clipboard
Copied
You can, for example, temporarily flatten group and then take a bounds.
However, there is a serious problem with Opacity Mask 😞
Copy link to clipboard
Copied
Yeah, I'm realizing now that this problem may be more difficult than I thought. The "select all" bounding box creates a bounding rectangle using the points of the visible art that are most extreme, but it does not include stroke. So even if I COULD get that specific bounding box, it might cut off some of the art, which is worse than having white space around it.
This is a tough one. I know how to see which art objects are clipping masks. If I could just figure out how to exclude all the art objects that are affected by those masks then I could exclude those and include the other art objects on the page. Then I'd have what I need.
Copy link to clipboard
Copied
Hi DB,
I'm trying to solve the same issue you described; and I have discovered the same complexities that you have. Did you ever figure out the code to do this?
Thanks!
-- Jim
Copy link to clipboard
Copied
I never found a solution. I'm pretty sure it's impossible. I even contacted adobe and they pretty much told me that it couldn't be done with the current sdk. I'm haven't delved much into scripting but I doubt there's anything in there that would do it.
Copy link to clipboard
Copied
Iterate and add up all clipping path bounds using:
sAIArt->GetArtTransformBounds and specify the flag to include stroke in the bound.
Copy link to clipboard
Copied
Thanks for the follow-up DB!
After some research, I think that one can probably write some code to approximate a visible bounding box rectangle by parsing each piece of art and then have it work out what clipping masks and compound paths interact with it. Sadly, too much work for too little reward for me at this point...
Cheers!
-- Jim
Copy link to clipboard
Copied
This one caused me a bit of a headache and I thought the least I could do was share the solution. If anyone sees any flaws in this please do point them out. The function below iteratively checks the tree to items to ensure that all parents have been taken into account and reports back the actual visible area.
The user passes in the AiArt of interest, a ptr to a bounds rect and the initialbounds should be 'true' when called outside.
ASErr GetVisibleBounds(AIArtHandle aiArt, AIRealRect* bounds, bool initBounds = true);
ASErr GetVisibleBounds(AIArtHandle aiArt, AIRealRect* bounds, bool initBounds)
{
//Get the bounds of an AiArt taking into account any clipping
ASErr error = kNoErr;
if(initBounds)
{
//First time in? Then init the bounds, the default setting
if(!aiArt)
{
return kBadParameterErr;
}
error = sAIArt->GetArtBounds( aiArt, bounds );
if(error){
return error;
}
}
//In case a null is passed in for a parent, just return, its not really an error.
//See where we call the function recursively below.
if(!aiArt)
{
return error;
}
//Set the art target for bounds as the current input
AIArtHandle aiArtBounds = aiArt;
//Get the item's parent
AIArtHandle aiArtParent = nullptr;
error = sAIArt->GetArtParent( aiArtBounds, &aiArtParent );
if(error){
return error;
}
//See if the item is a group
short artType = kUnknownArt;
error = sAIArt->GetArtType(aiArtBounds, &artType);
if(error){
return error;
}
//See if it is a group, if its a group it will have the clipping child within itself, so carry on
if(artType != kGroupArt)
{
//Its *not* a group, use its parent when checking the clipping bounds. The parent will hold the
//clipping child within itself
aiArtBounds = aiArtParent;
}
//Excessive safety check...
if(aiArtBounds)
{
//Ensure the parent group has its clipping taken into account, this will climb up the tree of items checking each group in
//turn as it finds them. Note: pass in false for the initBounds to ensure we don't mess up the initial bounds from the source item.
error = GetVisibleBounds(aiArtParent, bounds, false);
if(error)
{
return error;
}
//Ensure we are only looking for clipping in a Group, can't think how we could up up here without it being a group, but
//who knows what tomorrow will bring.
short artTargetType = kUnknownArt;
error = sAIArt->GetArtType(aiArtBounds, &artTargetType);
if(artTargetType == kGroupArt)
{
//See if the group has a group clipping item
AIBoolean clippedGrp = false;
error = sAIGroupSuite->GetGroupClipped( aiArtBounds,&clippedGrp );
if(!error && clippedGrp)
{
//It does, iterate over all the children, usually its the 1st one, but not always. Also, in case its possible to have
//2 clipping items, best ensure we check all children. Safety First!
AIArtHandle child = nullptr;
sAIArt->GetArtFirstChild(aiArtBounds, &child);
while(child)
{
if(sAIArt->IsArtClipping(child))
{
//Found a clipping child, get its bounds and update the incoming items to within them if needs be
AIRealRect clippingBounds;
error = sAIArt->GetArtBounds( child, &clippingBounds );
if(!error)
{
//Clip to the bounds
if(clippingBounds.left > bounds->left ) bounds->left = clippingBounds.left;
if(clippingBounds.bottom > bounds->bottom ) bounds->bottom = clippingBounds.bottom;
if(clippingBounds.right < bounds->right ) bounds->right = clippingBounds.right;
if(clippingBounds.top < bounds->top ) bounds->top = clippingBounds.top;
}
}
//Get the next item, and try again
AIArtHandle sibling = nullptr;
error = sAIArt->GetArtSibling(child, &sibling );
child = sibling;
}
}
}
}
return error;
}
Copy link to clipboard
Copied
This thread may also be of interest: Determine if an art object is hidden due to being clipped
Copy link to clipboard
Copied
Yep seems to do roughly the same thing. I might add in the math intersection stuff, its neater.
Thanks
Find more inspiration, events, and resources on the new Adobe Community
Explore Now