• Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
    Dedicated community for Japanese speakers
  • 한국 커뮤니티
    Dedicated community for Korean speakers
Exit
0

Text is not getting shown via API; even though it's getting saved.

Community Beginner ,
Dec 22, 2017 Dec 22, 2017

Copy link to clipboard

Copied

Hi,

I'm experiencing a very unusual issue when try ing to create and display text via the Adobe Text Engine API. Please see the steps below explaining the scenario

Step 1: Opening the .ai file a first time in order to test the plugin.

Text for the following fields should be created and displayed (but it's not)

  • Substrate
  • Note
  • Color Station
  • Date Created
  • Author

Also, text for File Name should be removed, but it's not.

1.PNG

Step 2: Deleting the plugin.

In order to confirm that the plugin code doesn't rerun, I deleted it before reopening the .ai file to see if any changes were saved.

2.PNG

Step 3: Reopening the .ai file without the plugin to check if the changes were saved

As seen below, all text fields to be shown were actually saved and the file name was removed too.

3.PNG

It all seemed to work fine previously and I'm puzzled by this! Any help would be truly appreciated!

Here's the code snippet I've used:

ATE::ICharFeatures features;

FontRef fontRef;

AIFontKey fontKey;

features.SetFontSize(10);

AIArtHandle newFrame;

SnpArtHelper artHelper;

/*

CODE FOR REMOVING TEXT

...............................................

*/

if (bClearTextFields)

{

     if (tmpFontSize == fontSize)

     {

          // Remove the text if the font size matches the configured one!

          textRange.Remove();

     }

}

/*

CODE FOR CREATING TEXT

...............................................

*/

// Get the group art that contains all the art in the current layer.

AIArtHandle artGroup = NULL;

result = sAIArt->GetFirstArtOfLayer(NULL, &artGroup);

aisdk::check_ai_error(result);

// Add the new point text item to the layer.

AITextOrientation orient = kHorizontalTextOrientation;

AIArtHandle textFrame = NULL;

result = sAITextFrame->NewPointText(kPlaceAboveAll, artGroup, orient, newAnchor, &textFrame);

aisdk::check_ai_error(result);

// Set the contents of the text range.

TextRangeRef range = NULL;

result = sAITextFrame->GetATETextRange(textFrame, &range);

aisdk::check_ai_error(result);

ITextRange crange(range);

string text = "1";

crange.SetLocalCharFeatures(features);

crange.InsertAfter(ai::UnicodeString(text).as_ASUnicode().c_str());

/*

CODE FOR SAVING THE FILE

...................................

*/

// TODOs: Ask client if the file is to be autosaved.

boolean bAutoSaveFile = true;

if (bAutoSaveFile)

{

     // Save the .AI file once showing

     AIDocumentHandle documentHandle;

     sAIDocument->GetDocument(&documentHandle);

     sAIDocumentList->Save(documentHandle);

}

TOPICS
SDK

Views

1.4K

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines

correct answers 1 Correct answer

Advocate , Jan 13, 2018 Jan 13, 2018

I tried your code and found a couple of minor issues:

1. I wasn't getting the text to appear even without the save until I changed this line:

tmpCRange.InsertAfter(ai::UnicodeString(tmpText).as_ASUnicode().c_str());

to (I guess the temporary unicodestring was going out of scope):

ai::UnicodeString uniStr(tmpText);

tmpCRange.InsertAfter(uniStr.as_ASUnicode().c_str());

2. This line is not really valid:

result = sAITextFrame->NewPointText(kPlaceAboveAll, tmpArtGroup, tmpOrient, tmpAnchor, &tmpTextFrame);

k

...

Votes

Translate

Translate
Adobe
Community Beginner ,
Jan 08, 2018 Jan 08, 2018

Copy link to clipboard

Copied

I just found out that the above issue only occurs if I call the sAIDocumentList->Save(documentHandle) function. It works fine otherwise. Any tips would be appreciated!

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Advocate ,
Jan 10, 2018 Jan 10, 2018

Copy link to clipboard

Copied

You could try calling sAIUser->AppIdle() and/or sAIDocument->SyncDocument() before sAIDocumentList->Save.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Jan 10, 2018 Jan 10, 2018

Copy link to clipboard

Copied

Thank you for your response. I tried calling sAIUser->AppIde(), then tried calling sAIDocument->SyncDocument() before sAIDocumentList->Save() but it didn't work. I also tried calling both functions at the same time but still no luck.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Jan 11, 2018 Jan 11, 2018

Copy link to clipboard

Copied

Could anyone please help me out here? I've tried calling a lot of functions, sAIUser->AppIdle(), sAIDocument->SyncDocument(),  sAIAppContext->SyncAndDraw(), sAIDocument->RedrawDocument() in both orders, before and after the sAIDocumentList->Save() function but it doesn't work. Calling the sAIDocumentList->Save() function just prevents all API text changes from getting shown. What's causing this and what's the solution? We need to have the autosave feature enabled for this, unfortunately.

Thank you.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Advocate ,
Jan 11, 2018 Jan 11, 2018

Copy link to clipboard

Copied

When does you code get called? From a plugin message? Which one? Is all the code called synchronously in the order shown?

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Jan 11, 2018 Jan 11, 2018

Copy link to clipboard

Copied

I'll paste in the entire .cpp file content. Basically, it's an extension of the sample SnippetRunner.cpp:

//========================================================================================

// 

//  $File: //ai_stream/rel_21_0/devtech/sdk/public/samplecode/SnippetRunner/Source/SnippetRunnerPlugin.cpp $

//

//  $Revision: #1 $

//

//  Copyright 1987 Adobe Systems Incorporated. All rights reserved.

// 

//  NOTICE:  Adobe permits you to use, modify, and distribute this file in accordance

//  with the terms of the Adobe license agreement accompanying it.  If you have received

//  this file from a source other than Adobe, then your use, modification, or

//  distribution of it requires the prior written permission of Adobe.

// 

//========================================================================================

#include "IllustratorSDK.h"

#include "AICSXS.h"

#include "SDKDef.h"

#include "SDKAboutPluginsHelper.h"

#include "SnippetRunnerID.h"

#include "SnippetRunnerPlugin.h"

#include "SnippetRunnerSuites.h"

#include "SnippetRunnerPanelController.h"

#include "SnippetRunnerLog.h"

#include "SnippetRunnerParameter.h"

#include "SnippetRunnerPreferences.h"

#include "SDKErrors.h"

#include "SnippetRunnerUnitTestManager.h"

// Framework includes:

#include "SnpRunnable.h"

#include "SnpSelectionHelper.h"

#include "SnpArtSetHelper.h"

#include "SnpArtHelper.h"

#include "tinyxml2.h"

#include "PdmReaderWriter.h"

using namespace ATE;

using namespace PdmMetaDataTools;

extern ImportSuite gPostStartupSuites[];

SnippetRunnerPlugin* gPlugin = NULL;

/*

*/

Plugin* AllocatePlugin(SPPluginRef pluginRef)

{

return new SnippetRunnerPlugin(pluginRef);

}

/*

*/

void FixupReload(Plugin* plugin)

{

SnippetRunnerPlugin::FixupVTable((SnippetRunnerPlugin*)plugin);

}

/*

*/

ASErr SnippetRunnerPlugin::SetGlobal(Plugin *plugin)

{

gPlugin = (SnippetRunnerPlugin *)plugin;

return kNoErr;

}

/*

*/

SnippetRunnerPlugin::SnippetRunnerPlugin(SPPluginRef pluginRef) :

Plugin(pluginRef),

fSnippetRunnerPanelController(NULL),

fCSXSPlugPlugSetupCompleteNotifier(NULL)

{

strncpy(fPluginName, kSnippetRunnerPluginName, kMaxStringLength);

fAboutPluginMenu = NULL;

fShowHidePanelMenu = NULL;

}

/*

*/

SnippetRunnerPlugin::~SnippetRunnerPlugin()

{

}

/*

*/

ASErr SnippetRunnerPlugin::Message(char *caller, char *selector, void *message)

{

ASErr result = kNoErr;

try {

result = Plugin::Message(caller, selector, message);

}

catch (ai::Error& ex) {

result = ex;

}

catch (...) {

result = kCantHappenErr;

}

if (result) {

if (result == kUnhandledMsgErr) {

// Defined by Plugin.hpp and used in Plugin::Message - ignore

result = kNoErr;

}

else {

// TODO should really call gPlugin->ReportError

aisdk::report_error(result);

}

}

return result;

}

/*

*/

ASErr SnippetRunnerPlugin::GoMenuItem(AIMenuMessage *message)

{

ASErr result = kNoErr;

try {

if (message->menuItem == this->fAboutPluginMenu)

{

SDKAboutPluginsHelper aboutPluginsHelper;

aboutPluginsHelper.PopAboutBox(message, "About SnippetRunner", kSDKDefAboutSDKCompanyPluginsAlertString);

}

else if (message->menuItem == this->fShowHidePanelMenu)

{

if (this->fSnippetRunnerPanelController)

{

AIBoolean visible = false;

fSnippetRunnerPanelController->IsPrimaryStageVisible(visible);

if (visible)

{

fSnippetRunnerPanelController->UnloadExtension();

}

else

{

fSnippetRunnerPanelController->LoadExtension();

}

}

}

}

catch (ai::Error& ex) {

result = ex;

}

return result;

}

/*

*/

ASErr SnippetRunnerPlugin::UpdateMenuItem(AIMenuMessage *message)

{

// Update the Show/Hide panel menu.

AIBoolean visible = false;

if (fSnippetRunnerPanelController != NULL)

fSnippetRunnerPanelController->IsPrimaryStageVisible(visible);

// sAIMenu->CheckItem(fShowHidePanelMenu, visible);

return kNoErr;

}

/*

*/

ASErr SnippetRunnerPlugin::StartupPlugin(SPInterfaceMessage *message)

{

ASErr error = kNoErr;

try {

error = Plugin::StartupPlugin(message);

aisdk::check_ai_error(error);

error = this->AddNotifiers(message);

aisdk::check_ai_error(error);

// error = this->AddMenus(message);

// aisdk::check_ai_error(error);

}

catch (ai::Error& ex) {

error = ex;

}

return error;

}

/*

*/

ASErr SnippetRunnerPlugin::ShutdownPlugin(SPInterfaceMessage *message)

{

ASErr error = kNoErr;

if (fSnippetRunnerPanelController)

{

fSnippetRunnerPanelController->RemoveEventListeners();

delete fSnippetRunnerPanelController;

fSnippetRunnerPanelController = NULL;

Plugin::LockPlugin(false);

}

this->ReleasePostStartupSuites();

error = Plugin::ShutdownPlugin(message);

return error;

}

ASErr SnippetRunnerPlugin::PostStartupPlugin()

{

ASErr error = kNoErr;

try {

// Acquire suites we could not get on plug-in startup.

error = this->AcquirePostStartupSuites();

if (error) {

error = kNoErr;

// Ignore - this should not be a fatal error.

// We want the SnippetRunner panel to open and

// we want snippets to be checking that the

// suite pointers they depend on are not NULL

// before using them.

}

// Read preferences.

SnippetRunnerPreferences::Instance();

// Create flash panel

if (fSnippetRunnerPanelController == NULL)

{

fSnippetRunnerPanelController = new SnippetRunnerPanelController();

error = Plugin::LockPlugin(true);

if (error) { return error; }

}

}

catch (ai::Error ex) {

error = ex;

}

return error;

}

/*

*/

ASErr SnippetRunnerPlugin::AddNotifiers(SPInterfaceMessage *message)

{

ASErr error = kNoErr;

try {

error = sAINotifier->AddNotifier(message->d.self, "SnippetRunner " kAIApplicationShutdownNotifier, kAIApplicationShutdownNotifier, &fAppShutdownNotifier);

aisdk::check_ai_error(error);

error = sAINotifier->AddNotifier(message->d.self, "SnippetRunner " kAIArtSelectionChangedNotifier, kAIArtSelectionChangedNotifier, &fArtSelectionChangedNotifier);

aisdk::check_ai_error(error);

error = sAINotifier->AddNotifier(message->d.self, "SnippetRunner " kAICSXSPlugPlugSetupCompleteNotifier, kAICSXSPlugPlugSetupCompleteNotifier, &fCSXSPlugPlugSetupCompleteNotifier);

aisdk::check_ai_error(error);

error = sAINotifier->AddNotifier(message->d.self, "SnippetRunner",

kAIDocumentOpenedNotifier, NULL);

error = sAINotifier->AddNotifier(message->d.self, "SnippetRunner",

kAIDocumentSavedNotifier, NULL);

// error = sAINotifier->AddNotifier(message->d.self, "SnippetRunner", kAIDocumentNewNotifier, NULL);

}

catch (ai::Error& ex) {

error = ex;

}

return error;

}

/*

*/

ASErr SnippetRunnerPlugin::Notify(AINotifierMessage *message)

{

ASErr error = kNoErr;

if (message->notifier == fAppShutdownNotifier)

{

SnippetRunnerLog::DeleteInstance();

SnippetRunnerParameter::DeleteInstance();

SnippetRunnerPreferences::Instance()->DeleteInstance();

SnippetRunnerUnitTestManager::Instance()->DeleteInstance();

}

else if (message->notifier == fArtSelectionChangedNotifier)

{

fSnippetRunnerPanelController->HandleModelChanged();

}

else if (message->notifier == fCSXSPlugPlugSetupCompleteNotifier)

{

fSnippetRunnerPanelController->RegisterCSXSEventListeners();

}

if (!strcmp(message->type, kAIDocumentOpenedNotifier))

{

ai::FilePath filePath;

sAIDocument->GetDocumentFileSpecification(filePath);

/*

1. DEFINE ALL ROW AND COLUMN HEADER NAMES BASED UPON VARIABLE NAMES

headerInfoByVariables = {

'varname1': ['Brand', 'Signature:']

'varname2': ['Brand'], 'Approved:'],

}

2. WHEN ITERATING THROUGH THE XML

i. CREATE A NEW ARRAY BY FLIPPLING THE ABOVE ARRAY

variableNamesByHeaders = {

'Brand': {

'Signature': ["varname1", "varval1"],

'Approved': "varname2", "varval2"]

}

''

}

ii. Create row header strings

iii. Create col header strings

*/

// //sAIUser->MessageAlert(ai::UnicodeString("File opened"));

// std::map<string, map<string, AIArtHandle>> textFrames = {};

map < string, map<string, PDMVariable>> variablesByHeaders = {};

map <string, PDMVariable> variablesByNewInfo = {};

tinyxml2::XMLDocument* doc;

// std::map<string, tinyxml2::XMLElement*> xmlTextVariables;

AIReal defaultDateHOffset = -30.0;

AIReal defaultCheckboxHOffset = -5.0;

AIReal defaultCheckboxVOffset = -5.0;

map<string, TitleBlockHeaderInfo> headerInfoByVariables = {

{ "xmp:MarketingAppReq",{ "Product \rMarketing ", "Approval\rNeeded", true, defaultCheckboxHOffset, defaultCheckboxVOffset } },

{ "xmp:MarketingApproval",{ "Product \rMarketing ", "Approved By:", false, 0.0, 0.0 } },

{ "xmp:MarketingAppDate",{ "Product \rMarketing ", "Date:", false, defaultDateHOffset, 0 } },

{ "xmp:MktgBrandAppReq",{ "Brand \rMarketing ", "Approval\rNeeded", true, defaultCheckboxHOffset, defaultCheckboxVOffset } },

{ "xmp:MktgBrandApproval",{ "Brand \rMarketing ", "Approved By:", false, 0.0, 0.0 } },

{ "xmp:MktgBrandAppDate",{ "Brand \rMarketing ", "Date:", false, defaultDateHOffset, 0 } },

{ "xmp:MarketingDirAppReq",{ "Marketing\rDirector ", "Approval\rNeeded", true, defaultCheckboxHOffset, defaultCheckboxVOffset } },

{ "xmp:MarketingDirApproval",{ "Marketing\rDirector ", "Approved By:", false, 0.0, 0.0 } },

{ "xmp:MarketingDirAppDate",{ "Marketing\rDirector ", "Date:", false, defaultDateHOffset, 0 } },

{ "xmp:EngineeringAppReq",{ "Engineering", "Approval\rNeeded", true, defaultCheckboxHOffset, defaultCheckboxVOffset + 5 } },

{ "xmp:EngineeringApproval",{ "Engineering", "Approved By:", false, 0.0, 0.0 } },

{ "xmp:EngAppDate",{ "Engineering", "Date:", false, defaultDateHOffset, 0 } },

{ "xmp:OperationsDirAppReq",{ "Operations\rDirector", "Approval\rNeeded", true, defaultCheckboxHOffset, defaultCheckboxVOffset } },

{ "xmp:OperationsDirApproval",{ "Operations\rDirector", "Approved By:", false, 0.0, 0.0 } },

{ "xmp:OperationsDirAppDate",{ "Operations\rDirector", "Date:", false, defaultDateHOffset, 0 } },

{ "xmp:QAAppReq",{ "Product\rIntegrity", "Approval\rNeeded", true, defaultCheckboxHOffset, defaultCheckboxVOffset } },

{ "xmp:QAApproval",{ "Product\rIntegrity", "Approved By:", false, 0.0, 0.0 } },

{ "xmp:QAAppDate",{ "Product\rIntegrity", "Date:", false, defaultDateHOffset, 0 } },

{ "xmp:QADirAppReq",{ "Quality\rManger", "Approval\rNeeded", true, defaultCheckboxHOffset, defaultCheckboxVOffset } },

{ "xmp:QADirApproval",{ "Quality\rManger", "Approved By:", false, 0.0, 0.0 } },

{ "xmp:QADirAppDate",{ "Quality\rManger", "Date:", false, defaultDateHOffset, 0 } },

{ "xmp:LegalAppReq",{ "Legal", "Approval\rNeeded", true, defaultCheckboxHOffset, defaultCheckboxVOffset + 5 } },

{ "xmp:LegalApproval",{ "Legal", "Approved By:", false, 0.0, 0.0 } },

{ "xmp:LegalAppDate",{ "Legal", "Date:", false, defaultDateHOffset, 0 } },

{ "xmp:MexicoAppReq",{ "KCM", "Approval\rNeeded", true, defaultCheckboxHOffset, defaultCheckboxVOffset + 5 } },

{ "xmp:MexicoApproval",{ "KCM", "Approved By:", false, 0.0, 0.0 } },

{ "xmp:MexicoAppDate",{ "KCM", "Date:", false, defaultDateHOffset, 0 } },

{ "xmp:ToolingApproved",{ "FINAL ROUTING", "Approval\rNeeded", true, defaultCheckboxHOffset, defaultCheckboxVOffset + 8 } },

};

map<string, string> newInfoByVariables = {

{ "xmp:fileNameOnSystem", "File Name:" },

{ "xmp:DrawnDate", "Date Created:" },

{ "xmp:DrawnBy", "Author:" },

{ "xmp:SpecNo", "Spec #" },

{ "xmp:LegacySpecNo", "Legacy Spec #" },

{ "xmp:Revision", "Revision:" },

{ "dc:description[1]", "Description:" },

{ "xmp:ECnumber", "EC #:" },

{ "xmp:Project", "Project #:" },

{ "xmp:ProjectDescription", "Project Description:" },

{ "xmp:ArtworkType", "Artwork Type:" },

{ "xmp:Dieline", "Die/Drawing:" },

{ "xmp:Material", "Substrate:" },

{ "xmp:TemplateNo", "Template:" },

{ "xmp:TemplateRev", "Template Rev:" },

{ "xmp:PrintProcess", "Print Process:" },

{ "xmp:Note", "Note: " },

{ "xmp:ColorStations", "Color Stations: " },

{ "xmp:Size", "Size:" },

{ "xmp:ApplicationVersion", "Application Version:" }

};

std::map<string, boolean> textToRemove = {

{ "Illustrator Spec Box 20171005 - Copy", true },

{ "Illustrator/CC", true },

};

std::map <string, AIReal> hColHeaders = {};

std::map <string, AIReal> vRowHeaders = {};

std::map <string, AIRealPoint> newInfoCoordinates = {};

/*

NOTE: Separate variables are used below because of the following reason:

fullPathUnicode.as_Platform().c_str() will not really give you a

compile error but will say within the debugger watch window:

"Nested function evaluation not supported."

This was the main cause of the crash bug - xml being assigned the value of

a 'Nested Function Evaluation'

*/

ai::UnicodeString fullPathUnicode = filePath.GetFullPath(true);

string strFullpath = fullPathUnicode.as_Platform();

const char* fullFilePath = strFullpath.c_str();

// //sAIUser->MessageAlert(ai::UnicodeString("A document was just opened!"));

ASErr result = kNoErr;

try

{

// Fill these up with the required values above.

/*

static const char* xml =

"<root>"

"<debug>Filepath passed = C:\dev\desktopDev\lampros\evenfloPdm\aiConnectorDllTest\aiConnectorDllTest\bin\Debug\./example.ai&#x0A;No smart handler available. trying packet scanning.&#x0A;&#x0A;CreatorTool = Adobe Illustrator CC 2017 (Windows)&#x0A;terminated successfully after reading values&#x0A;</debug>"

"<message></message>"

"<pdm_variable_list>"

"<pdm_variable>"

"<variable_name>xmp:MarketingAppReq</variable_name>"

"<variable_value>03/21/2017</variable_value>"

"</pdm_variable>"

"<pdm_variable>"

"<variable_name>xmp:MarketingApproval</variable_name>"

"<variable_value>03/21/2017</variable_value>"

"</pdm_variable>"

"</pdm_variable_list>"

"</root>";

*/

// Determine whether or not to clear the text fields

string loggedInUser = "some_user";

string userThatCheckedOut = "some_user";

bool fileIsCheckedOut = false;

bool fileOpenedOnce = false;

string strXml = MetaDataReaderWriter::preCheckIn(fullFilePath);

const char* xml = strXml.c_str();

/***********************************************************

////////////////// BEGIN EXTRACTING VARIABLE CONTENT FROM THE XML CONTENT//////////////

***********************************************************/

//sAIUser->MessageAlert(ai::UnicodeString("Step 1 -PARSING XML: First, We'll display all variable names and their values found in the XML."));

doc = new tinyxml2::XMLDocument();

doc->Parse(xml);

// doc->LoadFile("return_std__string_checkin_result.xml"); //  xml.css");

// tinyxml2::XMLElement* pdm_variable_list = doc->NextSiblingElement()->NextSiblingElement()->NextSiblingElement();

tinyxml2::XMLElement* root = doc->FirstChildElement("root");

tinyxml2::XMLElement* pdm_variable_list = root->FirstChildElement("pdm_variable_list");

for (tinyxml2::XMLElement* pdm_variable = pdm_variable_list->FirstChildElement("pdm_variable"); pdm_variable != NULL; pdm_variable = pdm_variable->NextSiblingElement())

{

tinyxml2::XMLElement* variable_name = pdm_variable->FirstChildElement("variable_name");

tinyxml2::XMLElement* variable_value = pdm_variable->FirstChildElement("variable_value");

tinyxml2::XMLNode* textNodeVarName = variable_name->FirstChild();

string var_name = "";

if (textNodeVarName)

{

tinyxml2::XMLText* xmlTextVarName = textNodeVarName->ToText();

var_name = xmlTextVarName->Value();

tinyxml2::XMLNode* textNodeVarValue = variable_value->FirstChild();

string var_value = "";

if (textNodeVarValue)

{

tinyxml2::XMLText* xmlTextVarValue = textNodeVarValue->ToText();

var_value = xmlTextVarValue->Value();

}

if (headerInfoByVariables.find(var_name) != headerInfoByVariables.end())

{

string rowHeader = headerInfoByVariables[var_name].row;

string columnHeader = headerInfoByVariables[var_name].column;

variablesByHeaders[rowHeader][columnHeader] = { var_name, var_value };

vRowHeaders[rowHeader] = 0.0;

hColHeaders[columnHeader] = 0.0;

// xmlTextVariables[var_name] = variable_value;

string alertMsg = "'" + var_name + "' - '" + var_value + "'";

//sAIUser->MessageAlert(ai::UnicodeString(alertMsg.c_str()));

}

if (newInfoByVariables.find(var_name) != newInfoByVariables.end())

{

string tmpHeader = newInfoByVariables[var_name];

variablesByNewInfo[tmpHeader] = { var_name, var_value };

// xmlTextVariables[var_name] = variable_value;

newInfoCoordinates[tmpHeader] = { 0.0, 0.0 };

string alertMsg = "'" + var_name + "' - '" + var_value + "'";

//sAIUser->MessageAlert(ai::UnicodeString(alertMsg.c_str()));

}

/*if (var_name == "dc:format")

{

string tmpDcFormat = var_value;

bool isDCFormat = var_value == "1";

}

*/

if (var_name == "xmp:loggedInUser")

{

loggedInUser = var_value;

}

else if (var_name == "xmp:userThatCheckedOut")

{

userThatCheckedOut = var_value;

}

else if (var_name == "xmp:fileIsCheckedOut")

{

fileIsCheckedOut = var_value == "1";

}

if (var_name == "xmp:fileOpenedOnce" && var_value == "1")

{

fileOpenedOnce = true;

}

}

}

bool bClearTextFields = !loggedInUser.compare(userThatCheckedOut) && fileIsCheckedOut;

////////////////// END EXTRACTING VARIABLE CONTENT FROM THE XML CONTENT//////////////

ATE::ICharFeatures features;

FontRef fontRef;

AIFontKey fontKey;

features.SetFontSize(10);

AIRealMatrix headerMatrix;

AIArtHandle artGroup = NULL;

result = sAIArt->GetFirstArtOfLayer(NULL, &artGroup);

SDK_ASSERT(sAITextFrame);

SnippetRunnerLog* log = SnippetRunnerLog::Instance();

AIArtSpec specs[1] = { { kTextFrameArt,0,0 } };

SnpArtSetHelper textFrameArtSet(specs, 1);

if (textFrameArtSet.GetCount() > 0)

{

//sAIUser->MessageAlert(ai::UnicodeString("Step 2 - PARSING AI FILE: Next, we'll search for text tokens corresponding to variable names and display their captions and coordinates."));

/***********************************************************

////////////////// BEGIN ITERATING THE ENTIRE TEXT OF THE .AI FILE//////////////

// 1. GETTING COORDINATES FOR HEADER ROWS AND COLUMNS

// 2. GETTING COORDINATES FOR ALL OTHER REQUIRED FIELDS

***********************************************************/

for (size_t i = 0; i < textFrameArtSet.GetCount(); i++)

{

AIArtHandle textFrameArt = textFrameArtSet;

TextFrameRef textFrameRef = NULL;

result = sAITextFrame->GetATETextFrame(textFrameArt, &textFrameRef);

aisdk::check_ai_error(result);

ITextFrame textFrame(textFrameRef);

SnippetRunnerLog::Indent indent;

ITextRange textRange = textFrame.GetTextRange();

ai::AutoBuffer<ASUnicode> contents(textRange.GetSize());

textRange.GetContents(contents, textRange.GetSize());

ASInt32 strLength = textRange.GetSize();

if (strLength > 0)

{

std::vector<char> vc(strLength);

ASInt32 conLength = textRange.GetContents(&vc[0], strLength);

if (conLength == strLength)

{

std::string contents;

contents.assign(vc.begin(), vc.begin() + strLength);

int tmp = 0;

bool bColumnIndexPresent = hColHeaders.find(contents) != hColHeaders.end();

bool bRowIndexPresent = vRowHeaders.find(contents) != vRowHeaders.end();

if (bRowIndexPresent || bColumnIndexPresent)

{

AIArtHandle frameArt = NULL;

error = sAITextFrame->GetAITextFrame(textFrameRef, &frameArt);

AIRealPoint anchor;

result = sAITextFrame->GetPointTextAnchor(textFrameArt, &anchor);

aisdk::check_ai_error(result);

if (bColumnIndexPresent)

{

hColHeaders[contents] = anchor.h;

headerMatrix = textFrame.GetMatrix();

string alertMsg = "'" + contents + " - x-coord: " + std::to_string(anchor.h);

//sAIUser->MessageAlert(ai::UnicodeString(alertMsg.c_str()));

}

if (bRowIndexPresent)

{

vRowHeaders[contents] = anchor.v;

string alertMsg = "'" + contents + "' - y-coord: " + std::to_string(anchor.v);

//sAIUser->MessageAlert(ai::UnicodeString(alertMsg.c_str()));

}

}

bool bNewInfoIndexPresent = variablesByNewInfo.find(contents) != variablesByNewInfo.end();

if (bNewInfoIndexPresent)

{

AIArtHandle frameArt = NULL;

error = sAITextFrame->GetAITextFrame(textFrameRef, &frameArt);

AIRealPoint anchor;

result = sAITextFrame->GetPointTextAnchor(textFrameArt, &anchor);

aisdk::check_ai_error(result);

int len = contents.length();

float widthFactor = 4.7;

anchor.h += len * widthFactor;

newInfoCoordinates[contents] = anchor;

string alertMsg = "'" + contents + "' - x: " + std::to_string(anchor.h) + ", y: " + std::to_string(anchor.v);

//sAIUser->MessageAlert(ai::UnicodeString(alertMsg.c_str()));

}

// First, we get the features for the text range

ATE::ICharFeatures tmpFeatures = textRange.GetUniqueLocalCharFeatures();

// And then we somehow compare those with the features that are set later

bool bBuff;

AIReal tmpFontSize = tmpFeatures.GetFontSize(&bBuff);

AIReal fontSize = features.GetFontSize(&bBuff);

if (bClearTextFields)

{

if (tmpFontSize == fontSize)

{

// Remove the text if the font size matches the configured one!

textRange.Remove();

}

}

// Removing any unneeded text

bool bTextToRemove = false; // textToRemove.find(contents) != textToRemove.end();

if (bTextToRemove)

{

textRange.Remove();

}

}

}

}

////////////////// END ITERATING THE ENTIRE TEXT OF THE .AI FILE//////////////

/**********************************************************************

///////////// BEGIN CREATING TEXT FIELDS FOR TABLE ///////////////////////

************************************************************************/

ATE::ICharFeatures features;

FontRef fontRef;

AIFontKey fontKey;

features.SetFontSize(10);

map<string, AIReal>::iterator row;

//sAIUser->MessageAlert(ai::UnicodeString("Step 3 - SHOWING DATA: Finally, we'll show values at the coordinates fetched for each of the variable names."));

for (row = vRowHeaders.begin(); row != vRowHeaders.end(); row++)

{

std::string rowIndex = row->first;

AIReal rowTop = row->second; // rowRectTop.top;

map<string, AIReal>::iterator col;

map<string, AIArtHandle> innerTextRange;

for (col = hColHeaders.begin(); col != hColHeaders.end(); col++)

{

std::string colIndex = col->first;

AIReal colLeft = col->second;

if (variablesByHeaders[rowIndex].find(colIndex) != variablesByHeaders[rowIndex].end())

{

string text = variablesByHeaders[rowIndex][colIndex].value;

string varName = variablesByHeaders[rowIndex][colIndex].name;

AIReal hOffset = headerInfoByVariables[varName].hOffset;

AIReal vOffset = headerInfoByVariables[varName].vOffset;

AIArtHandle newFrame;

SnpArtHelper artHelper;

// Get the group art that contains all the art in the current layer.

AIArtHandle artGroup = NULL;

result = sAIArt->GetFirstArtOfLayer(NULL, &artGroup);

aisdk::check_ai_error(result);

//

// Add the new point text item to the layer.

AITextOrientation orient = kHorizontalTextOrientation;

AIRealPoint newAnchor = {};

newAnchor.h = colLeft + hOffset;

newAnchor.v = rowTop + vOffset;

AIArtHandle textFrame = NULL;

result = sAITextFrame->NewPointText(kPlaceAboveAll, artGroup, orient, newAnchor, &textFrame);

aisdk::check_ai_error(result);

// Set the contents of the text range.

TextRangeRef range = NULL;

result = sAITextFrame->GetATETextRange(textFrame, &range);

aisdk::check_ai_error(result);

ITextRange crange(range);

if (headerInfoByVariables[varName].bCheckbox)

{

text = text == "1" ? "X" : "";

}

crange.SetLocalCharFeatures(features);

crange.InsertAfter(ai::UnicodeString(text).as_ASUnicode().c_str());

innerTextRange[colIndex] = textFrame;

string alertMsg = "'" + varName + "' - '" + text + "' - Coords: (" + std::to_string(newAnchor.h) + "," + std::to_string(newAnchor.v) + ")";

//sAIUser->MessageAlert(ai::UnicodeString(alertMsg));

}

}

// textFrames[rowIndex] = innerTextRange;

}

/**********************************************************************

///////////// END CREATING TEXT FIELDS FOR TABLE ///////////////////////

************************************************************************/

/***********************************************************************

///////////////////// BEGIN CREATING NEW FIELDS

***********************************************************************/

map<string, AIRealPoint>::iterator itNew;

for (itNew = newInfoCoordinates.begin(); itNew != newInfoCoordinates.end(); itNew++)

{

std::string newInfoHeader = itNew->first;

AIRealPoint newAnchor = itNew->second;

AIArtHandle newFrame;

SnpArtHelper artHelper;

// Get the group art that contains all the art in the current layer.

AIArtHandle artGroup = NULL;

result = sAIArt->GetFirstArtOfLayer(NULL, &artGroup);

aisdk::check_ai_error(result);

// Add the new point text item to the layer.

AITextOrientation orient = kHorizontalTextOrientation;

AIArtHandle textFrame = NULL;

result = sAITextFrame->NewPointText(kPlaceAboveAll, artGroup, orient, newAnchor, &textFrame);

aisdk::check_ai_error(result);

// Set the contents of the text range.

TextRangeRef range = NULL;

result = sAITextFrame->GetATETextRange(textFrame, &range);

aisdk::check_ai_error(result);

ITextRange crange(range);

string text = "";

string varName = "";

if (variablesByNewInfo.find(newInfoHeader) != variablesByNewInfo.end())

{

text = variablesByNewInfo[newInfoHeader].value;

varName = variablesByNewInfo[newInfoHeader].name;

crange.SetLocalCharFeatures(features);

crange.InsertAfter(ai::UnicodeString(text).as_ASUnicode().c_str());

string alertMsg = "'" + varName + "' - '" + text + "' - Coords: (" + std::to_string(newAnchor.h) + "," + std::to_string(newAnchor.v) + ")";

//sAIUser->MessageAlert(ai::UnicodeString(alertMsg));

}

}

/***********************************************************************

///////////////////// END CREATING NEW FIELDS

***********************************************************************/

AIDocumentHandle documentHandle;

sAIDocument->GetDocument(&documentHandle);

// TODOs: Ask client if the file is to be autosaved.

boolean bAutoSaveFile = true;

if (bAutoSaveFile)

{

// Save the .AI file once showing

// WARNING:

// This function seems to be the reason the text doesn't show up!

// Let's see if there's an alternative

// sAIUser->AppIdle();

// sAIDocument->SyncDocument();

// sAIDocumentList->Save(documentHandle);

}

// fileOpenedOnce = true;

if (!fileOpenedOnce)

{

// sAIDocument->SetDocumentModified(true);

AIBoolean foundAnyLegacy;

sAIDocumentList->Save(documentHandle);

sAILegacyTextConversion->ConvertAllToNative(&foundAnyLegacy);

// sAIAppContext->SyncAndDraw();

// sAIDocumentList->Close(documentHandle);

// sAIDocumentList->CloseAll();

// ToDo:

// Write 1 to the xmp:fileOpenedOnce variable

// in order to avoid inifinite close/open chain!

std::string xmlVariableStringExample = "<pdm_variable_list> <pdm_variable> <variable_name>xmp:fileOpenedOnce</variable_name> <variable_value>1</variable_value> </pdm_variable> </pdm_variable_list>";

std::string resultStatus = MetaDataReaderWriter::sync(fullFilePath, xmlVariableStringExample.c_str());

//sAIDocumentList->OpenNthRecentDocument(0);

}

}

else

{

log->Write("Create some text art.");

aisdk::check_ai_error(kBadParameterErr);

}

}

catch (ai::Error& ex)

{

result = ex;

}

catch (ATE::Exception& ex)

{

result = ex.error;

}

return result;

}

else if (!strcmp(message->type, kAIDocumentSavedNotifier))

{

// sAIDocument->ResumeTextReflow();

// sAIDocument->RedrawDocument();

}

// else if (!strcmp(message->type, kAIDocumentSavedNotifier))

// {

/*

// To Curtis Mimes: Here's where the Save event is triggered.

ASErr result = kNoErr;

const char* fullFilePath = filePath.GetFullPath().as_Platform().c_str();

tinyxml2::XMLPrinter streamer;

doc->Print(&streamer);

// //sAIUser->MessageAlert(ai::UnicodeString(streamer.CStr()));

map<string, map<string, AIArtHandle>>::iterator outerTextRangeIterator;

for (outerTextRangeIterator = textFrames.begin(); outerTextRangeIterator != textFrames.end(); outerTextRangeIterator++)

{

std::string rowIndex = outerTextRangeIterator->first;

map<string, AIArtHandle> outerTextRange = outerTextRangeIterator->second; // rowRectTop.top;

map<string, AIArtHandle>::iterator innerTextRangeIterator;

for (innerTextRangeIterator = outerTextRange.begin(); innerTextRangeIterator != outerTextRange.end(); innerTextRangeIterator++)

{

std::string colIndex = innerTextRangeIterator->first;

AIArtHandle textFrame = innerTextRangeIterator->second;

TextRangeRef range = NULL;

result = sAITextFrame->GetATETextRange(textFrame, &range);

aisdk::check_ai_error(result);

ITextRange textRange(range);

ai::AutoBuffer<ASUnicode> contents(textRange.GetSize());

textRange.GetContents(contents, textRange.GetSize());

// //sAIUser->MessageAlert(ai::UnicodeString(contents));

ASInt32 strLength = textRange.GetSize();

if (strLength > 0)

{

std::vector<char> vc(strLength);

ASInt32 conLength = textRange.GetContents(&vc[0], strLength);

if (conLength == strLength)

{

std::string contents;

contents.assign(vc.begin(), vc.begin() + strLength);

// To Curtis Mimes: And Here's where the modified text field content gets displayed (the contents variable).

string prevVarValue = variablesByHeaders[rowIndex][colIndex].value;

if (contents != prevVarValue)

{

string variableName = variablesByHeaders[rowIndex][colIndex].name;

// //sAIUser->MessageAlert(ai::UnicodeString(contents));

xmlTextVariables[variableName]->SetText(contents.c_str());

}

}

}

}

}

tinyxml2::XMLPrinter streamer2;

doc->Print(&streamer2);

//sAIUser->MessageAlert(ai::UnicodeString(streamer2.CStr()));

// MetaDataReaderWriter::sync(fullFilePath, streamer2.CStr());

*/

// }

// else if (!strcmp(message->type, kAIDocumentNewNotifier))

// {

// int documentNew = 1;

// }

return error;

}

/*

*/

ASErr SnippetRunnerPlugin::AddMenus(SPInterfaceMessage *message)

{

ASErr error = kNoErr;

// Add About Plugins menu item for this plug-in.

SDKAboutPluginsHelper aboutPluginsHelper;

error = aboutPluginsHelper.AddAboutPluginsMenuItem(message,

kSDKDefAboutSDKCompanyPluginsGroupName,

ai::UnicodeString(kSDKDefAboutSDKCompanyPluginsGroupNameString),

kSnippetRunnerPluginName "...",

&fAboutPluginMenu);

aisdk::check_ai_error(error);

// Add an SDK menu group to the Windows menu.

const char* kSDKWindowsMenuGroup = "SDKWindowGroup";

bool exists = false;

error = this->MenuGroupExists(kSDKWindowsMenuGroup, exists);

aisdk::check_ai_error(error);

if (!exists) {

AIPlatformAddMenuItemDataUS menuItemData;

menuItemData.groupName = kOtherPalettesMenuGroup;

menuItemData.itemText = ai::UnicodeString("SDK");

AIMenuItemHandle menuItemHandle = nil;

// error = sAIMenu->AddMenuItem(message->d.self, NULL, &menuItemData, kMenuItemNoOptions, &menuItemHandle);

// aisdk::check_ai_error(error);

AIMenuGroup menuGroup = nil;

// error = sAIMenu->AddMenuGroupAsSubMenu(kSDKWindowsMenuGroup, kMenuGroupSortedAlphabeticallyOption, menuItemHandle, &menuGroup);

// aisdk::check_ai_error(error);

}

// Add menu item for this plug-in under the company's about plug-ins menu group.

AIPlatformAddMenuItemDataUS showHidePanelMenuData;

showHidePanelMenuData.groupName = kSDKWindowsMenuGroup;

showHidePanelMenuData.itemText = ai::UnicodeString(kSnippetRunnerPluginName);

AIMenuItemHandle showHidePanelMenuItemHandle = nil;

// error = sAIMenu->AddMenuItem(message->d.self, NULL, &showHidePanelMenuData, kMenuItemWantsUpdateOption, &showHidePanelMenuItemHandle);

// aisdk::check_ai_error(error);

fShowHidePanelMenu = showHidePanelMenuItemHandle;

return error;

}

/*

*/

AIErr SnippetRunnerPlugin::MenuGroupExists(const char* targetGroupName, bool& groupAlreadyMade)

{

AIErr error = kNoErr;

groupAlreadyMade = false;

ai::int32 count = 0;

AIMenuGroup dummyGroup = nil;

error = sAIMenu->CountMenuGroups(&count);

if (error) return error;

for (ai::int32 i = 0; i < count; i++)

{

error = sAIMenu->GetNthMenuGroup(i, &dummyGroup);

aisdk::check_ai_error(error);

const char* name;

error = sAIMenu->GetMenuGroupName(dummyGroup, &name);

aisdk::check_ai_error(error);

if (std::strcmp(name, targetGroupName) == 0)

{

groupAlreadyMade = true;

break;

}

}

return error;

}

/*

*/

ASErr SnippetRunnerPlugin::AcquirePostStartupSuites()

{

ASErr error = kNoErr;

for (int i = 0; gPostStartupSuites.name != nil; ++i) {

if (gPostStartupSuites.suite != nil) {

ASErr tmperr = sSPBasic->AcquireSuite(gPostStartupSuites.name,

gPostStartupSuites.version,

(const void **)gPostStartupSuites.suite);

SDK_ASSERT_MSG_NOTHROW(!tmperr,

aisdk::format_args("AcquireSuite failed for suite=%s version=%d",

gPostStartupSuites.name,

gPostStartupSuites.version));

if (tmperr && !error) {

// A suite could not be acquired - note first error encountered for later return to caller then carry on.

error = tmperr;

}

}

}

return error;

}

/*

*/

ASErr SnippetRunnerPlugin::ReleasePostStartupSuites()

{

ASErr error = kNoErr;

for (int i = 0; gPostStartupSuites.name != nil; ++i) {

if (gPostStartupSuites.suite != nil) {

void **s = (void **)gPostStartupSuites.suite;

if (*s != nil) {

ASErr tmperr = sSPBasic->ReleaseSuite(gPostStartupSuites.name, gPostStartupSuites.version);

*s = nil;

SDK_ASSERT_MSG_NOTHROW(!tmperr,

aisdk::format_args("ReleaseSuite failed for suite=%s version=%d",

gPostStartupSuites.name,

gPostStartupSuites.version));

if (tmperr && !error) {

// A suite could not be released - note first error encountered for later return to caller then carry on.

error = tmperr;

}

}

}

}

return error;

}

/*

*/

ASErr SnippetRunnerPlugin::GoTimer(AITimerMessage* message)

{

// This plug-in only has one timer - used for running unit tests.

SnippetRunnerUnitTestManager::Instance()->GoTimer(message);

return kNoErr;

}

/*

*/

void SnippetRunnerPlugin::NotifyLogChanged()

{

if (fSnippetRunnerPanelController)

{

fSnippetRunnerPanelController->HandleLogChanged();

}

}

/*

*/

void SnippetRunnerPlugin::NotifyEndUnitTest()

{

if (fSnippetRunnerPanelController)

{

fSnippetRunnerPanelController->HandleModelChanged();

}

}

// End SnippetRunnerPlugin.cpp

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Advocate ,
Jan 11, 2018 Jan 11, 2018

Copy link to clipboard

Copied

So you are calling your code on a document open notification. What happens if you move your code into the About menu callback (replace the aboutPluginsHelper.PopAboutBox line with your code). If you then open a document and invoke the about menu, does it work correctly?

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Jan 11, 2018 Jan 11, 2018

Copy link to clipboard

Copied

No, it's giving the same problem. Here are the steps I performed:

1) I moved over the code into the About Menu callback (like you suggested).

2) I opened the document and invoked the about menu (with the save() function called). It did not show any text, indicating the same problem.

3) I closed the document, deleted the plugin, reopened the document and the text was there, indicating that the changes were actually saved.

4) I removed the save() function, rebuilt and copied over the plugin, then reopened another copy of the original file and the text again showed up, indicating that the save() function is the cause.

Any other debugging tips?

Thank you for your help!

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Advocate ,
Jan 12, 2018 Jan 12, 2018

Copy link to clipboard

Copied

I thought it might be something to do with you calling sAIDocumentList->Save from a document open notifier, but it seems that is not the issue.

Why are you calling sAIDocumentList->Save again and then sAILegacyTextConversion->ConvertAllToNative after the save when fileOpenedOnce is false?

My next suggestion would be to remove all the XML/XMP code and just create a single text field and see if you still get the same issue.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Jan 12, 2018 Jan 12, 2018

Copy link to clipboard

Copied

Ok. I've removed all XML/XMP code and created a single text field using the code below. Again, the field did get displayed without calling the save() function. And as before, it didn't show up when the save() function was called.

AIRealPoint tmpAnchor;

  tmpAnchor.h = 528.66622161865234;

  tmpAnchor.v = -668.91015625000000;

  string tmpText = "This is some temp text!";

  // Get the group art that contains all the art in the current layer.

  AIArtHandle tmpArtGroup = NULL;

  result = sAIArt->GetFirstArtOfLayer(NULL, &tmpArtGroup);

  aisdk::check_ai_error(result);

  // Add the new point text item to the layer.

  AITextOrientation tmpOrient = kHorizontalTextOrientation;

  AIArtHandle tmpTextFrame = NULL;

  result = sAITextFrame->NewPointText(kPlaceAboveAll, tmpArtGroup, tmpOrient, tmpAnchor, &tmpTextFrame);

  aisdk::check_ai_error(result);

  TextRangeRef tmpRange = NULL;

  result = sAITextFrame->GetATETextRange(tmpTextFrame, &tmpRange);

  aisdk::check_ai_error(result);

  ITextRange tmpCRange(tmpRange);

  tmpCRange.InsertAfter(ai::UnicodeString(tmpText).as_ASUnicode().c_str());

  AIDocumentHandle documentHandle;

  sAIDocument->GetDocument(&documentHandle);

  sAIDocumentList->Save(documentHandle);

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Advocate ,
Jan 13, 2018 Jan 13, 2018

Copy link to clipboard

Copied

I tried your code and found a couple of minor issues:

1. I wasn't getting the text to appear even without the save until I changed this line:

tmpCRange.InsertAfter(ai::UnicodeString(tmpText).as_ASUnicode().c_str());

to (I guess the temporary unicodestring was going out of scope):

ai::UnicodeString uniStr(tmpText);

tmpCRange.InsertAfter(uniStr.as_ASUnicode().c_str());

2. This line is not really valid:

result = sAITextFrame->NewPointText(kPlaceAboveAll, tmpArtGroup, tmpOrient, tmpAnchor, &tmpTextFrame);

kPlaceAboveAll means place above all other art and the second argument should be NULL in this case. If you want to put the art at the top of tmpArtGroup, you should use kPlaceInsideOnTop.

With both these changes I was still seeing the same behaviour as you. Not sure why this is happening, but the "zero timer" workaround seems to work. This can be used to separate two pieces of code that interfere with each other.

Replace this code:

AIDocumentHandle documentHandle;

sAIDocument->GetDocument(&documentHandle);

sAIDocumentList->Save(documentHandle);

with:

AITimerHandle timer;

sAITimer->AddTimer(message->d.self,"SaveTimer",0,&timer);

Then in the GoTimer function replace:

SnippetRunnerUnitTestManager::Instance()->GoTimer(message);

with:

AIDocumentHandle documentHandle;

sAIDocument->GetDocument(&documentHandle);

sAIDocumentList->Save(documentHandle);

sAITimer->SetTimerActive(message->timer,false);

Don't forget the last line to deactivate the timer or it will repeat forever.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Jan 16, 2018 Jan 16, 2018

Copy link to clipboard

Copied

LATEST

The workaround you explained resolved the issue. Thank you very much for following up and getting this fixed!

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines