Extract Page Name Attribute

Hi All,

I am Referencing geometry from Layout Views to Grasshopper and it would be extremely useful to be able to extract the Layout Name in order to organize in a smart way the referenced geometry.

I believe there is not an existing component for this but I’m sure a bit of coding would solve this easily.

The Attribute User Text in Rhino allows you to use the Key Page Name which is what I need but if you read that Value in Grasshopper you don’t get the actual name but something like:

%<PageName (“ID”)>%

Any idea of how could I get the actual Layout Name?




yes this is quite simple. This example works for Rhino6 only.

ExtractUserText.gh (4.5 KB)

var obj = RhinoDocument.Objects.FindId(id);
A = obj.Attributes.GetUserString(key);

Thanks for your answer Tom,

I get the same problem than before. Because the attribute is automatically generated by Rhino through the text field command, I don’t get the Page Name but the same message than before. It seems to work only when you type the attribute. Any idea?

seems to be a reference to the page object. you’ll need to parse the guid out and find the page object. I’ll see if I find time later on

Yeah it seems to be not as straight forward.

I want to do diagrams referencing geometry from the layouts and use the Grasshopper preview to display that geometry in the correct Layout. Solving this is crucial. My only option now is working with parent layers, which is not ok…

If you have the time to give a hand I would highly appreciate it!


Did you get this resolved ? I have come across the same issue…

No it didn’t get solved. I imagine won’t be hard to code.


Sorry for not replying. Probally haven’t had time… :thinking:

Its actually quite easy:

laynameExtract.gh (5.3 KB)

using System;
using System.Collections;
using System.Collections.Generic;

using Rhino;
using Rhino.Geometry;

using Grasshopper;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Data;
using Grasshopper.Kernel.Types;

using Rhino.Display;

/// <summary>
/// This class will be instantiated on demand by the Script component.
/// </summary>
public class Script_Instance : GH_ScriptInstance
#region Utility functions
  /// <summary>Print a String to the [Out] Parameter of the Script component.</summary>
  /// <param name="text">String to print.</param>
  private void Print(string text) { /* Implementation hidden. */ }
  /// <summary>Print a formatted String to the [Out] Parameter of the Script component.</summary>
  /// <param name="format">String format.</param>
  /// <param name="args">Formatting parameters.</param>
  private void Print(string format, params object[] args) { /* Implementation hidden. */ }
  /// <summary>Print useful information about an object instance to the [Out] Parameter of the Script component. </summary>
  /// <param name="obj">Object instance to parse.</param>
  private void Reflect(object obj) { /* Implementation hidden. */ }
  /// <summary>Print the signatures of all the overloads of a specific method to the [Out] Parameter of the Script component. </summary>
  /// <param name="obj">Object instance to parse.</param>
  private void Reflect(object obj, string method_name) { /* Implementation hidden. */ }

#region Members
  /// <summary>Gets the current Rhino document.</summary>
  private readonly RhinoDoc RhinoDocument;
  /// <summary>Gets the Grasshopper document that owns this script.</summary>
  private readonly GH_Document GrasshopperDocument;
  /// <summary>Gets the Grasshopper script component that owns this script.</summary>
  private readonly IGH_Component Component;
  /// <summary>
  /// Gets the current iteration count. The first call to RunScript() is associated with Iteration==0.
  /// Any subsequent call within the same solution will increment the Iteration count.
  /// </summary>
  private readonly int Iteration;

  /// <summary>
  /// This procedure contains the user code. Input parameters are provided as regular arguments,
  /// Output parameters as ref arguments. You don't have to assign output parameters,
  /// they will have a default value.
  /// </summary>
  private void RunScript(Guid id, ref object PageName)
    PageName = "(Visible on all layout views)";

    var obj = RhinoDocument.Objects.FindId(id);
    Guid vid = obj.Attributes.ViewportId;
    if (vid != Guid.Empty)
      RhinoPageView view = RhinoDoc.ActiveDoc.Views.Find(vid) as RhinoPageView;
      PageName = view.PageName;


  // <Custom additional code> 

  // </Custom additional code> 


Hi Tom,

Yes that does the job perfectly! Thank you.

I remember when I asked about this I was trying to control diagram graphics (arrows, etc.) on layouts using grasshopper. It was useful to read the layout name to then display the graphics on the right layout.

I abandoned this workflow as it was getting so convoluted but I’ll let you know if I give it another go (if you are interested).


Yeah why not.

Scripting in that sense is not diffcult! You just need to know where the information is within the library and some simple syntax rules of the language you choose (which is C# in that case)
Here is whats going on:

  • You input a (global unique) id of your ‘RhinoObject’ in Grasshopper and pass it to the input port of your ScriptComponent -> RunScript(Guid id, ref object PageName)

  • In order to work with it you need to get the actual objects instance:
    var obj = RhinoDocument.Objects.FindId(id);

  • Now any Rhino object consists of the actual geometry and some meta data (Layer, Color …).
    To access the meta data you need to call the “Attributes” property of your Rhino object instance.

  • In there you find a ‘ViewportId’ property. If the ViewportId is set (so you have no empty GUID) it means it only shows up on a certain viewport. (This information is written down by the developers in the api documention. )

  • A layout view, is also a viewport. Now since we have a viewport id, we can use that to find the corresponding RhinoPageView instance on your active document.

  • Once we have this we just extract the page name and pass it to our output port called ‘PageName’. PageName = view.PageName

Essentially any properties related to Rhinoobjects (Style, Layers, Meta data …) are somewhere in that object. Rhinoobjects are stored in (active) Rhinodocuments, such as other things to managed them better (Views, Layers) etc. Usually they are linked by ids, which is a bit unusual, but probably because of persistance (saving, loading) reasons.
Rhino uses inheritance quite extensive, that is why you need cast them from time to time. So if you choose to go for C# one day, it might be a good idea to understand this concept as well. And of course understanding where and what is in the holy grail of Rhino: https://developer.rhino3d.com/api/ helps a lot. Essential things like “baking” are quite simple features (= adding Rhinoobjects to the Document) .

1 Like

Hi Tom,

Thanks for your explanation, it really helps to understand the idea behind it.

I am comfortable in Grasshopper but limited somehow to the components available + plugins. I have done some Python tutorials but I’m not quite getting it yet.

This reminds me to keep pushing on this! Thanks again!