Copy link to clipboard
Copied
x=app.activeDocument.viewPreferences.horizontalMeasurementUnits
Result: PICAS
// so far, so good. Just to be sure...
x.toString()
Result: PICAS
// now
x+x
Result: 4108374726
// huh?
x+x.toString()
Result: 2054187363PICAS
// and finally
"units are "+x
Result: units are 2054187363
I suppose there's some explanation why the measurement units for picas is secretly associated
with the integer 2054187363 and why it's a good idea.
Hi all,
Here are additional details on Enumeration and Enumerator objects in CS5 and later:
http://www.indiscripts.com/post/2012/07/indesign-scripting-forum-roundup-3#hd1sb1
@+
Marc
Copy link to clipboard
Copied
The answer is simply that MeasurementUnits is an enumerator. So really
the fundamental value of each particular MeasurementUnit is an integer,
as is the case for any enumerator.
When you write x = myDoc.viewPreferences.horizontalMeasurementUnits,
InDesign is actually doing you a favour in that it returns something
comprehensible (PICAS, CM, etc.). In fact, if I recall correctly, in
older versions (CS4), you'd only ever get the enum value.
So in fact it is possible to write:
myDoc.viewPreferences.horizontalMeasurementUnits = 2053335395 and it
will be set to Ciceros, in this case. (All the numbers are listed in the
OMV in ESTK).
If you write:
app.viewPreferences.horizontalMeasurementUnits.valueOf()
You'll get the actual enumerator number.
However, because Javascript is freely typed, it will helpfully convert
something to a string if you alert() it, or display it in the Console, etc.
So, as per your examples: if you add two enumerators, the interpreter
does not do a toString(), hence you get an integer.
If you add an enum to a string (as in the second example), it's like
adding any number to a string: 5+"z" will give: 5z.
In the last example, the enum remains an integer.
Copy link to clipboard
Copied
This almost makes sense, but the behavior of the enumerator object
is odd with respect to the + operator. What is the point of giving an
enum any meaning as an integer at all?
for most objects, object+object becomes object.toString()+object.toString(),
so what magic causes this enumerator to be interpreted as an integer, and why is that desirable?
Copy link to clipboard
Copied
That's just what an enumerator is: "An enumeration consists of a set of
named integer constants."
It's a convenient way of referring to a number with a more memorable
name. But the underlying value of any element in an enumerator is an
integer.
There's nothing surprising about this.
Note that:
MeasurementUnits.INCHES === 2053729891
returns true.
Copy link to clipboard
Copied
these enums are not integers, they are objects. Somewhere in the bowels of their
behavior, unlike most objects, they respond to + as an integer rather than as a string.
My guess is that adobe engineers, when they were adapting existing internals to a javascript interface,
found that then needed to do range checks on measurement units (ie; existing C code was something
like)
enum measurementunit { picas=1, points=2, inches=3 }
and code existed that did things like
if(units>points) {}
and for whatever reason, they decided this semantics needed to be visible to the javascript binding. It's a crappy
reason, but at least it's a reason.
---
Final watchword on the subject - these objects are complete liars. Try this:
var x = MeasurementUnits.picas;
var y = 2054187363;
var z = "2054187363";
typeof x is object
typeof y is number
typeof z is string
x==y is true
x===y is also true !
x===z is also true !
y===z is false (as expected)
so we've broken a basic identity - these enums are identical to many things that are not identical to each other.
Copy link to clipboard
Copied
Hi all,
Here are additional details on Enumeration and Enumerator objects in CS5 and later:
http://www.indiscripts.com/post/2012/07/indesign-scripting-forum-roundup-3#hd1sb1
@+
Marc
Copy link to clipboard
Copied
Enumerators do not call .toString() automatically. Why is that, i have no idea, but i agree it is bad, and can lead to very strange behaviour.
Your MeasurmentUnits example is just the tip of the iceberg. Worst culprits are the SpecialCharacters enumerator and the worst of the worst: NothingEnum.NOTHING that gets returned in some cases. The big problem with NOTHING is that, when converted it gets evaluated to True, even if it is a considered a falsy value. Even "better", the same member, on different objects will sometimes return Nothing and other times will return null, for example text.appliedCharaterStyle will return null, but findTextPreferences.appliedCharacterStyle willl return NothingEnum.NOTHING
Copy link to clipboard
Copied
While this does seem to describe the behavior of MeasurementUnit.picas, it still doesn't make sense
that "picas" and friends should have these I'm-also-a-number behaviors that do make sense for special
characters.
Copy link to clipboard
Copied
> it still doesn't make sense that "picas" and friends should have these
> I'm-also-a-number behaviors that do make sense for special characters
The I'm-also-a-number behavior does not actually make sense whatever the enum you consider, it's just the way those objects work.
Note, however, that Enumerator instances do not have the cast Number (the typeof operator does return 'object'). What has been done when introducing these objects in CS5 is:
1. Making myEnum.valueOf() return a Number.
2. Overriding == and === so that myEnum can match a Number in both equal and strict-equal comparisons.
The reason for this, IMHO, was to preserve compatibility with previous versions (CS4 and before) where Enumerations—the collection of enums—were just pointing out to numeric values, that is, Numbers.
Thus, a syntax like (myUnit===MeasurementUnits.INCHES), or (myUnit===2053729891), or even (MeasurementUnits.INCHES===2053729891), still works from CS4 to CS5 without breaking existing code.
Now, about the meaning of these numbers, we know they are nothing but unique identifiers. Technically—and this is an old tradition in Adobe architecture—such identifiers are all built based on 4-character sequences.
In the case of measurement units, identifiers are formed using the prefix 'z' and 3 relevant letters from the unit name, usually the leading letters, e.g. 'zinc' for inches, 'zpic' for picas, and so on.
Then, ASCII codes of these characters are taken to build a number:
INCHES => z i n c => 7A 69 6E 63 => 0x7A696E63 (=2053729891 in decimal)
PICAS => z p i c => 7A 70 69 63 => 0x7A706963 (=2054187363 in decimal)
etc.
And you will check that +MeasurementUnits.INCHES is 0x7A696E63.
Anyway, your comment #5 highlights something else, based on the code:
var x = MeasurementUnits.picas;
var y = 2054187363;
var z = "2054187363";
We have checked, indeed, that x===y is true (see reason 2 above).
But we also can check, as you mention, that x===z is true!!! Which is not CS4-backwards-compatible at all and sounds highly irrelevant since x.toString()==='PICAS'.
I suspect that this strange case is a side effect of how the === operator has been overloaded. In my opinion, the hidden function Enumerator['==='](arg) mutely coerces the argument into a Number whatever it actually is. So the string z is simply taken as +z and that's why x===z also returns true. What Adobe should have done instead is:
// JS pseudo code
Enumerator['==='] = function(arg){ return 'string'==typeof arg ? this.toString()==arg : this.valueOf()==+arg };
…and then x===z would have been false, and x==='PICAS' would have been true 😉
@+
Marc
Copy link to clipboard
Copied
Great explanation, Marc!
Copy link to clipboard
Copied
I guess we've reached the point of exhaustion on this. All I really wanted was, when packaging a rectangle
object to also display the units, I was shocked that defining the
toString() { return("left+","+right+" "+width+"x"+height+" "+units) }
printed a random looking number as the units.