Getting text layer shadow parameters (ExtendScript CS5, Photoshop scripting) - extendscript

Is there any way to get text(or any other) layer shadow params in Adobe Photoshop CS5 using ExtendScript for further convertion to CSS3 like text string?
Thanks!

There is a way.
You have to use the ActionManager:
var ref = new ActionReference();
ref.putEnumerated( charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );
var desc = executeActionGet(ref).getObjectValue(stringIDToTypeID('layerEffects')).getObjectValue(stringIDToTypeID('dropShadow'));
desc.getUnitDoubleValue(stringIDToTypeID('distance'))
Where "dropShadow" is the layereffect you want to read and for example "distance" is the parameter that will be returned. Other layereffects and parameters are only known as eventids. Look in the documentation (bad documented) if you need other eventids.
The next AM-Code will check if there is a layerstyle shadow.
var res = false;
var ref = new ActionReference();
ref.putEnumerated( charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );
var hasFX = executeActionGet(ref).hasKey(stringIDToTypeID('layerEffects'));
if ( hasFX ){
var ref = new ActionReference();
ref.putEnumerated( charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );
res = executeActionGet(ref).getObjectValue(stringIDToTypeID('layerEffects')).hasKey(stringIDToTypeID('dropShadow'));
}
return res;
This will explain http://forums.adobe.com/thread/714406 more.
If you find a way to SET the shadow, without setting other params, let me know...

Probably not the answer you're looking for but there is really no way to access the individual properties of layer styles from extendscript. The only method in the API (as of CS6) that references layer styles is ArtLayer.applyStyle(name). You actually have to create a style in Photoshop and save to the palette by name in order to use this.
The only thing I can think of is to actually parse the .asl files found in adobe/Adobe Photoshop/presets/styles/ using C/C++. These files contain several layer styles saved in a proprietary format. I haven't found any libraries to parse these files but they may exist.

If you have Photoshop CS6.1 (or later), you can check out the implementation of the "Copy CSS to Clipboard" feature to see how to access the drop shadow parameters.
On Windows, the source code for this is in
Adobe Photoshop CS6\Required\CopyCSSToClipboard.jsx
On the Mac, the source code is in:
Adobe Photoshop CS6/Adobe Photoshop CS6.app/Contents/Required/CopyCSSToClipboard.jsx
(if you're looking in the Finder on the Mac, you'll need to control-click on the Photoshop app icon and select "Show Package Contents" to get to the Contents/Required folder).
Look for the routine cssToClip.addDropShadow for an example of how to extract the information. If you want to use routines from CopyCSSToClipboard.jsx in your own code, add the following snippet to your JSX file:
runCopyCSSFromScript = true;
if (typeof cssToClip == "undefined")
$.evalFile( app.path + "/" + localize("$$$/ScriptingSupport/Required=Required") + "/CopyCSSToClipboard.jsx" );
Also, at the bottom of CopyCSSToClipboard.jsx, there are sample calls to cssToClip.dumpLayerAttr. This is a useful way to explore parameters you may want to access from your scripts that aren't accessible from the Photoshop DOM.
Be forewarned that code in the Required folder is subject to change in future versions.

I was able to make an ActionPrinter method that dumps out a tree of all the data in an action using C# and the photoshop COM wrapper.
The PrintCurrentLayer method will dump all the data in a layer, including all of the Layer Effects data.
static void PrintCurrentLayer(Application ps)
{
var action = new ActionReference();
action.PutEnumerated(ps.CharIDToTypeID("Lyr "), ps.CharIDToTypeID("Ordn"), ps.CharIDToTypeID("Trgt"));
var desc = ps.ExecuteActionGet(action);//.GetObjectValue(ps.StringIDToTypeID("layerEffects"));//.getObjectValue(ps.StringIDToTypeID('dropShadow'));
ActionPrinter(desc);
}
static void ActionPrinter(ActionDescriptor action)
{
for (int i = 0; i < action.Count; i++)
{
var key = action.GetKey(i);
if (action.HasKey(key))
{
//var charId = action.Application.TypeIDToCharID((int)key);
//Debug.WriteLine(charId);
switch (action.GetType(key))
{
case PsDescValueType.psIntegerType:
Debug.WriteLine("{0}: {1}", (PSConstants)key, action.GetInteger(key));
break;
case PsDescValueType.psStringType:
Debug.WriteLine("{0}: \"{1}\"", (PSConstants)key, action.GetString(key));
break;
case PsDescValueType.psBooleanType:
Debug.WriteLine("{0}: {1}", (PSConstants)key, action.GetBoolean(key));
break;
case PsDescValueType.psDoubleType:
Debug.WriteLine("{0}: {1}", (PSConstants)key, action.GetDouble(key));
break;
case PsDescValueType.psUnitDoubleType:
Debug.WriteLine("{0}: {1} {2}", (PSConstants)key, action.GetUnitDoubleValue(key), (PSConstants)action.GetUnitDoubleType(key));
break;
case PsDescValueType.psEnumeratedType:
Debug.WriteLine("{0}: {1} {2}", (PSConstants)key, (PSConstants)action.GetEnumerationType(key), (PSConstants)action.GetEnumerationValue(key));
break;
case PsDescValueType.psObjectType:
Debug.WriteLine($"{(PSConstants)key}: {(PSConstants)action.GetObjectType(key)} ");
Debug.Indent();
ActionPrinter(action.GetObjectValue(key));
Debug.Unindent();
break;
case PsDescValueType.psListType:
var list = action.GetList(key);
Debug.WriteLine($"{(PSConstants)key}: List of {list.Count} Items");
Debug.Indent();
for (int count = 0; count < list.Count; count++)
{
var type = list.GetType(count);
Debug.WriteLine($"{count}: {type} ");
Debug.Indent();
switch (type)
{
case PsDescValueType.psObjectType:
ActionPrinter(list.GetObjectValue(count));
break;
case PsDescValueType.psReferenceType:
var reference = list.GetReference(count);
Debug.WriteLine(" Reference to a {0}", (PSConstants)reference.GetDesiredClass());
break;
case PsDescValueType.psEnumeratedType:
Debug.WriteLine(" {0} {1}", (PSConstants)list.GetEnumerationType(count), (PSConstants)list.GetEnumerationValue(count));
break;
default:
Debug.WriteLine($"UNHANDLED LIST TYPE {type}");
break;
}
Debug.Unindent();
}
Debug.Unindent();
break;
default:
Debug.WriteLine($"{(PSConstants)key} UNHANDLED TYPE {action.GetType(key)}");
break;
}
}
}
}

Related

What would be the reason that I can't make the ElementIDs of these objects in Revit match ones in a Revit file?

I am creating a plugin that makes use of the code available from BCFier to select elements from an external server version of the file and highlight them in a Revit view, except the elements are clearly not found in Revit as all elements appear and none are highlighted. The specific pieces of code I am using are:
private void SelectElements(Viewpoint v)
{
var elementsToSelect = new List<ElementId>();
var elementsToHide = new List<ElementId>();
var elementsToShow = new List<ElementId>();
var visibleElems = new FilteredElementCollector(OpenPlugin.doc, OpenPlugin.doc.ActiveView.Id)
.WhereElementIsNotElementType()
.WhereElementIsViewIndependent()
.ToElementIds()
.Where(e => OpenPlugin.doc.GetElement(e).CanBeHidden(OpenPlugin.doc.ActiveView)); //might affect performance, but it's necessary
bool canSetVisibility = (v.Components.Visibility != null &&
v.Components.Visibility.DefaultVisibility &&
v.Components.Visibility.Exceptions.Any());
bool canSetSelection = (v.Components.Selection != null && v.Components.Selection.Any());
//loop elements
foreach (var e in visibleElems)
{
//string guid = ExportUtils.GetExportId(OpenPlugin.doc, e).ToString();
var guid = IfcGuid.ToIfcGuid(ExportUtils.GetExportId(OpenPlugin.doc, e));
Trace.WriteLine(guid.ToString());
if (canSetVisibility)
{
if (v.Components.Visibility.DefaultVisibility)
{
if (v.Components.Visibility.Exceptions.Any(x => x.IfcGuid == guid))
elementsToHide.Add(e);
}
else
{
if (v.Components.Visibility.Exceptions.Any(x => x.IfcGuid == guid))
elementsToShow.Add(e);
}
}
if (canSetSelection)
{
if (v.Components.Selection.Any(x => x.IfcGuid == guid))
elementsToSelect.Add(e);
}
}
try
{
OpenPlugin.HandlerSelect.elementsToSelect = elementsToSelect;
OpenPlugin.HandlerSelect.elementsToHide = elementsToHide;
OpenPlugin.HandlerSelect.elementsToShow = elementsToShow;
OpenPlugin.selectEvent.Raise();
} catch (System.Exception ex)
{
TaskDialog.Show("Exception", ex.Message);
}
}
Which is the section that should filter the lists, which it does do as it produces IDs that look like this:
3GB5RcUGnAzQe9amE4i4IN
3GB5RcUGnAzQe9amE4i4Ib
3GB5RcUGnAzQe9amE4i4J6
3GB5RcUGnAzQe9amE4i4JH
3GB5RcUGnAzQe9amE4i4Ji
3GB5RcUGnAzQe9amE4i4J$
3GB5RcUGnAzQe9amE4i4GD
3GB5RcUGnAzQe9amE4i4Gy
3GB5RcUGnAzQe9amE4i4HM
3GB5RcUGnAzQe9amE4i4HX
3GB5RcUGnAzQe9amE4i4Hf
068MKId$X7hf9uMEB2S_no
The trouble with this is, comparing it to the list of IDs in the IFC file that we imported it from reveals that these IDs do not appear in the IFC file, and looking at it in Revit I found that none of the Guids in Revit weren't in the list that appeared either. Almost all the objects also matched the same main part of the IDs as well, and I'm not experienced enough to know how likely that is.
So my question is, is it something in this code that is an issue?
The IFC GUID is based on the Revit UniqueId but not identical. Please read about the Element Identifiers in RVT, IFC, NW and Forge to learn how they are connected.

Deserialize json one record at a time

I am working with large json files and memory is a concern. I would like to read one object into memory at a time from file. Is this possible?
In ServiceStack.Text docs it says there is an API using reader/stream
But I can't see how to get that working. The files are too large to deserialize in one go. Is it possible to handle this scenario with SS?
Thanks
No you'll want to use a streaming JSON parser like System.Text.Json Utf8JsonReader, this is the example on System.Text.Json introductory page:
byte[] data = Encoding.UTF8.GetBytes(json);
Utf8JsonReader reader = new Utf8JsonReader(data, isFinalBlock: true, state: default);
while (reader.Read())
{
Console.Write(reader.TokenType);
switch (reader.TokenType)
{
case JsonTokenType.PropertyName:
case JsonTokenType.String:
{
string text = reader.GetString();
Console.Write(" ");
Console.Write(text);
break;
}
case JsonTokenType.Number:
{
int value = reader.GetInt32();
Console.Write(" ");
Console.Write(value);
break;
}
// Other token types elided for brevity
}
Console.WriteLine();
}

GENERATE MULTIPLE TEXT FILES IN ACUMATICA LOCALLY

How can I generate several text files at the same time locally?
I am using the method:
throw new PXRedirectToFileException (file, true);
![enter image description here][1]
However, this method only generates 1 text file. I need more than 1 text file to be generated at a time.
List<object> data1099Misc = new List<object> { };
ARInvoice ari = Base.Document.Current;
foreach (xvrFSCab diot in PXSelect<xvrFSCab,
Where<xvrFSCab.invoiceNbr,
In<Required<xvrFSCab.invoiceNbr>>>>.Select(Base, ari.InvoiceNbr))
{
data1099Misc.Add(CreatePayerARecord(diot));
}
FixedLengthFile flatFile = new FixedLengthFile();
flatFile.WriteToFile(data1099Misc, sw);
sw.Flush();
sw.FlushAsync();
int cont = 0;
while ( cont<3)
{
cont = cont + 1;
string path = "DIOTJOSE" + ".txt";
PX.SM.FileInfo file = new PX.SM.FileInfo(path, null, stream.ToArray());
throw new PXRedirectToFileException(file, true);
}
Acumatica had the same issue when they had to open multiple reports at one click (with RedirectException).
For this reason Acumatica supports multiple RequiredException only for Reports.
They have a method called "CombineReport" that works with multiple PXReportRequiredException (PXReportsRedirectList)
Sad part is that they did not make something for other RequiredException or RedirectException
I tried to make my own "Combine" method but I was not able to create it just because the RedirectHelper.TryRedirect method use hardcoded types of the RedirectException inside body instead to use an generic or base object :(

Cocos2dx 3.4 - manipulating user input

I am working on a breakout-type game using Cocos2dx.
I need to make a highscore table. After the game is finished, I'd like to display a page, where player types his username into text field.
I don't know how to add the user input into variable, so I can manipulate it later (mainly saving along with score to display it on the selected scene).
What is the simplest way of doing so?
Approach One:
If you just need to handle keyboard as key-event, It's as easy as these below lines of code:
HelloWorld::init()
{
...
auto keyboardListener = EventListenerKeyboard::create();
keyboardListener->onKeyPressed = [](EventKeyboard::KeyCode keyCode, Event* event)
{
switch (keyCode)
{
case EventKeyboard::KeyCode::KEY_UP_ARROW: /*Jump maybe*/ break;
case EventKeyboard::KeyCode::KEY_DOWN_ARROW: /*Crouch maybe*/ break;
case EventKeyboard::KeyCode::KEY_RIGHT_ARROW: /*Move Right maybe*/ break;
case EventKeyboard::KeyCode::KEY_LEFT_ARROW: /*Move Left maybe*/ break;
}
};
_eventDispatcher->addEventListenerWithSceneGraphPriority(keyboardListener, this);
...
return true;
}
I think it's clear enough not to need any extra description.
Approach Two: if you need an input box that user/player can enter string with keyboard and you get what is entered, I recommend to use TextField which is available in cocos2d v3 ( and with some difficulty in v2) and has a full functionality. You can create and initial one of them as:
auto textField = cocos2d::ui::TextField::create("hint: enter here","Arial" , 30);
textField->setTextHorizontalAlignment(cocos2d::TextHAlignment::CENTER);
textField->setTextVerticalAlignment(cocos2d::TextVAlignment::CENTER);
textField->setColor(Color3B(100,100,100));
textField->setMaxLength(10);
textField->setMaxLengthEnabled(true);
textField->setTouchAreaEnabled(true);
textField->setTouchSize(Size(200,400));
textField->setPosition(...);
textField->addEventListener(CC_CALLBACK_2(HelloWorld::textFieldEvent, this));
this->addChild(textField, 10);
You can get entered data any time with std::string enteredData= textField->getString();
You can also do something when user entering text with two event as :
void HelloWorld::textFieldEvent(Ref *pSender, cocos2d::ui::TextField::EventType type)
{
switch (type)
{
case cocos2d::ui::TextField::EventType::ATTACH_WITH_IME:
{
textField->setColor(Color3B::BLACK);
// or whatever elese
break;
}
case cocos2d::ui::TextField::EventType::DETACH_WITH_IME:
{
textField->setColor(Color3B(100,100,100));
// or whatever elese
break;
}
}
}
Enjoy !

Remove an ICC profile from a PDF with ABCpdf

We have a small utility program that removes ICC profile, and I'm trying to optimize it.
The current program uses roughly this method to remove an ICC profile:
foreach (var item in doc.ObjectSoup)
{
if (item != null && doc.GetInfo(item.ID, "/ColorSpace*[0]*:Name").Equals("ICCBased", StringComparison.InvariantCultureIgnoreCase))
{
int profileId = doc.GetInfoInt(item.ID, "/ColorSpace*[1]:Ref"); // note the [1]: why is it there?
if (profileId != 0)
{
doc.GetInfo(profileId, "Decompress");
string profileData = doc.GetInfo(profileId, "Stream");
// this outputs the ICC profile raw data, with the profile's name somewhere up top
Console.WriteLine(string.Format("ICC profile for object ID {0}: {1}", item.ID, profileData));
doc.SetInfo(profileId, "Stream", string.Empty);
doc.GetInfo(profileId, "Compress");
}
}
}
Now, I want to optimize it, to be able to remove only some profiles (depending on the names), or only RGB profiles for instance (and keep CMYK ones). So I wanted to use actual objects :
foreach (var item in doc.ObjectSoup)
{
if (doc.GetInfo(item.ID, "Type") == "jpeg") // only work on PixMaps
{
PixMap pm = (PixMap)item;
if (pm.ColorSpaceType == ColorSpaceType.ICCBased)
{
// pm.ColorSpace.IccProfile is always null so I can't really set it to null or Recolor() it because it would change noting
// Also, there should already be an ICC profile (ColorSpaceType = ICCBased) but ColorSpace.IccProfile creates one (which is by design if there is none)
Console.WriteLine(string.Format("ICC profile for object ID {0}: {1}", item.ID, pm.ColorSpace.IccProfile));
}
}
}
Here is a sample program that showcases the problem : https://github.com/tbroust-trepia/abcpdf8-icc-profiles
Am I doing something wrong ? Is there something weird going on with the provided images ?
I'm using ABCPdf 8.
Thanks for your help.

Resources