Retrieve image attachments locations on an NSAttributedString - cocoa

I have an attributedString with image attachments on it, and I need to insert text to it without removing the images. The first thing that I'm doing is detecting at what indexes the images are currently attached.
Below is the code that I'm using, but it is not working all of the time, maybe there is a better way?
char charTemp;
for(int i = 0; i < [mutableAttStr.string length]; i++){
charTemp = [mutableAttStr.string characterAtIndex:i];
if (!isalpha(charTemp) && !isspace(charTemp) && !iscntrl(charTemp) && !ispunct(charTemp)) {
// NSLog(#"isAttachment");
[attachmentArrayCharIndex addObject:[NSNumber numberWithInt:i]];
}
}

First, use unichar, not char.
Second, NSTextAttachment.h declares NSAttachmentCharacter which you can use to determine where an attachment is located in the attributed string.

Related

Writing Macro in ImageJ to open, change color, and resave microscope images

I'm trying to write a code in Image J that will:
Open all images in separate windows that end in " - GFP.vsi" within a folder
Use look up tables to convert images to green and RGB color
From ImageJ, the commands are: run("Green"); and run("RGB Color");
Save each image in the same, original folder with the same name and as a .tif
Would be ok if images overwrite original, but would also be ok if they had a new name.
From ImageJ, to save as .tif file: saveAs("Tiff", "Filepath");
I have no experience with Java and just a little experience with coding. I tried to piece something together using code I found on stackoverflow and on the ImageJ website, but kept getting error codes. Any help is much appreciated!
This is the way I would approach this task:
macro "Change and Resave" {
dir1 = getDirectory("Choose Source Directory ");
dir2 = getDirectory("Choose Destination Directory ");
list = getFileList(dir1);
// Make an array of files ending " - GFP.vsi"
nd2list = newArray(0);
for (i=0; i<list.length; i++) {
if (endsWith(list[i], " - GFP.vsi")) {
vsilist = append(vsilist, list[i]);
}
}
setBatchMode(true);
// loop through files
for (i=0; i<vsilist.length; i++) {
showProgress(i+1, vsilist.length);
// open file using Bio-Formats, you may need to edit these two lines
s = "open=["+dir1+vsilist[i]+"] autoscale color_mode=Composite rois_import=[ROI manager] view=Hyperstack stack_order=XYCZT";
run("Bio-Formats Importer", s);
// your commands from your question
run("Green");
run("RGB Color");
saveAs("tiff", dir2+replace(vsilist[i],".vsi",".tif"));
close();
}
setBatchMode(false);
}
function append(arr, value) {
arr2 = newArray(arr.length+1);
for (i=0; i<arr.length; i++)
arr2[i] = arr[i];
arr2[arr.length] = value;
return arr2;
}

How to identify clipping masks in Photoshop using JavaScript

I've currently butchered a script I was reading that loops through a list of layers, and then looks for layers with a certain name (3/2, 4/3 etc). The next step is to check for layer masks that are clipped to the base layer, and merge them to it. I've read through the reference docs, and can't find anything about identifying clipping masks. I've attached an image as an example of how the document is structured.
And here is the code I have so far:
var doc = app.activeDocument
var ratios = ["1/1", "4/3", "3/4", "3/2", "2/3", "16/9", "9/3", "7/2", "11/5"];
for (var i = 0, il = doc.layers.length; i < il; i++) {
var curLayer = doc.layers[i];
for (var j = 0, jl = ratios.length; j < jl; j++) {
if (curLayer.name == ratios[j]) {
alert(curLayer.name);
// Check for clipping masks attached to this layer
}
}
}
I am using Photoshop CS5. Thanks!
I ended up working out another way to do it. I instead grouped the layers into a layerset, and exported them out of the document that way. For those that would like to see it, have a look here:
https://gist.github.com/BeauAgst/4da366b933cc75a0606a

inDesign JSX scripted add of heading and content into textFrame

I'm attempting to use inDesign JSX scripts to insert the following data into a document:
data = [{heading:"Heading 1", content: ["Some content"]},
{heading:"Heading 2", content: ["Some other content with", "Multiple paragraphs"]}]
The data has to be placed into a single TextFrame, but have different styling on the heading and content.
The only way I can see to add the text is in one go via the textFrame.contents variable:
allContent = "";
headingParagraphs = []; // keep track of which paragraphs are headings
paragraph = 0;
for (var i = 0; i < data.length; i++) {
allContent += data.heading + "\r"; // Use a newline to split the paragraph
headingParagraphs.push(paragraph);
paragraph++;
for (var j = 0; j < data.content.length; j++) {
allContent += data.content[j] + "\r"; // Use a newline to split the paragraph
paragraph++;
}
}
textFrame.contents = allContent; // all data is in, but all text is styled the same
Then once the data is in, I iterate the paragraphs and add some style to the headings:
for (var i = 0; i < textFrame.paragraphs.count(); i++) {
if (headingParagraphs.indexOf(i) != -1) { // this is a heading paragraph
textFrame.paragraphs[i].pointSize = 20;
}
}
This works fine for small data sets that fit on one page, but once the contents gets bigger than the frame, paragraphs only returns visible paragraphs. And if I follow on to a new textFrame, paragraphs get split and the headingParagraphs[] array no longer lines up.
Ideally I'd like to append to the contents and set styles before I append the next content - but the API docs aren't very clear on how you might do that (if at all)
// Pseudo code:
for all sections:
append the heading to the frame, split to next page if needed
style all the *new* paragraphs as headings
for all section contents
append the content to the frame, split to next page if needed
style any *new* paragraphs as normal content
Is there a way to achieve this using either an append function or some other way to assign headings to the right place after content has been added? Perhaps special characters in the content to define style?
Your longer text gets messed up because currently you are working inside a single text frame. As soon as the text runs out of this one frame, you can't refer to them as this frame's "owned" paragraphs anymore. Use parentStory instead, as it points to the whole story, inside one text frame or spanning more than one. It also keeps on working if the text gets overset.
So if you have a starting frame called textFrame, set a new variable story to textFrame.parentStory and use that to add text.
As for adding text to this frame(/story): indeed, there is no fast way to add formatted text. Setting contents only works for long swathes with the same formatting. One way I've used is to write INX formatted text to a temporary file and importing that. It's slow for short fragments, but larger stories (up to several hundreds of pages) can be created very efficiently in Javascript itself, and then importing it into ID is .. well, it aint fast but faster than trying to do it "manually".
The other way is to add contents one paragraph at a time. The trick is to set formatting and add your text to story.insertionPoints[-1]. This, in a particularly handy notation, refers to the very last text insertion point of the story. You can think of an insertion point as "the text cursor"; you can 'apply' formatting to it, and any text added will then have this formatting as well.
Your code snippet reworked to add one data item at a time:
for (var i = 0; i < data.length; i++)
{
story.insertionPoints[-1].pointSize = 20;
story.insertionPoints[-1].contents = data[i].heading + "\r"; // Use a newline to split the paragraph
story.insertionPoints[-1].pointSize = 10;
for (var j = 0; j < data[i].content.length; j++)
{
story.insertionPoints[-1].contents = data[i].content[j] + "\r"; // Use a newline to split the paragraph
}
}
One thing to note is that you cannot temporarily override the pointSize here. If you set it to your larger size, you must also set it back to the original size again (the '10' in my snippet).
Can I convince you to look in to using paragraph styles? With paragraph styles, you'd have something like
hdrStyle = app.activeDocument.paragraphStyles.item("Header");
textStyle = app.activeDocument.paragraphStyles.item("Text");
for (var i = 0; i < data.length; i++)
{
story.insertionPoints[-1].contents = data[i].heading + "\r"; // Use a newline to split the paragraph
story.insertionPoints[-2].appliedParagraphStyle = hdrStyle;
for (var j = 0; j < data[i].content.length; j++)
{
story.insertionPoints[-1].contents = data[i].content[j] + "\r"; // Use a newline to split the paragraph
story.insertionPoints[-2].appliedParagraphStyle = textStyle;
}
}
Note that it's worth here to invert inserting contents and applying formatting. This is so any previous 'temporary' formatting gets cleared; applying a paragraph style this way overrides any and all local overrides. As you have to apply the style to the previous paragraph (the one before the hard return you just inserted), you would use insertionPoints[-2] here.
The advantages of using styles over local formatting are countless. You can apply all formatting you want with a single command, safely remove all local overridden formatting, and change any part of the formatting globally if you are not satisfied with it, rather than having to re-run your script with slightly different settings.

Getting Glyph names using Core Text

For a small side project, I need to have all glyph names available in a font. So, I thought to make this:
CTFontRef f = CTFontCreateWithName(CFSTR("Zapfino"), 40, NULL);
CFIndex count = CTFontGetGlyphCount(f);
printf("There are %ld glyphs\n", count);
for (CGGlyph i = 1; i < count + 1; i++)
{
CTGlyphInfoRef gi = CTGlyphInfoCreateWithGlyph(i, f, CFSTR("(c)"));
CFStringRef name = CTGlyphInfoGetGlyphName(gi);
CGFontIndex idx = CTGlyphInfoGetCharacterIdentifier(gi);
printf("Glyph: %4hu, idx: %4hu ", i, idx);
CFShow(name);
printf("\n");
}
Creating the font and getting the glyph count works just fine. The name of the glyph is always NULL though. The CTGlyphInfoRef variable 'gi' gets a value. Any thoughts? What am I missing here?
You've misunderstood what CTGlyphInfo is about. It's not a way to query for information about a glyph. It's a representation of a substitution of a glyph for a string. You can set a glyph info object as the value for the kCTGlyphInfoAttributeName attribute in an attributed string. That glyph then replaces that range of attributed text, if the text matches the base string.
You can use CGFontCopyGlyphNameForGlyph() to get the name for a glyph from a font. You can create the CGFontRef directly using the CGFontCreate...() functions. You can also create one from a CTFont using CTFontCopyGraphicsFont().

How to display Images in a wxListCtrl (multicolumn report style)

I already managed to create wxListCtrls with either icons or multicolumn text like this
Picture of two wxListCtrls
Now I'd like to add an icon to each line of the text list on the left. I thought this should be possible as typical wxWidgets applications like code::blocks and wxSmith often diplay icons in list/tree views (resource browser window) and even in tabs of notebooks (compiler log window).
So how can I create something like this? (Everybody knows Windows Explorer)
Picture of Explorer Window with icons
I tried this...
SetImageList (ToolImages, wxIMAGE_LIST_NORMAL);
InsertColumn (0, "Icon");
SetColumnWidth (0, 40);
...
for (int i=0; i<5; i++)
{
InsertItem (i, i);
SetItemColumnImage (i, 0, i);
SetItem (i, 1, IntToStr (i+1));
...
But as you can see, only the text gets displayd, the image column is blank. Is it possible at all to mix text and images in report mode? If not, what other wxControl class can I use to get the desired result?
Many Thanks in advance.
Yes, it is possible, and the listctrl sample shows how to do it, in particular see MyFrame::InitWithReportItems() function. The only difference with your code seems to be that you use a different InsertItem() overload, so perhaps you should use InsertItem(i, "") instead.
Also check that your image list does have the 5 icons in it.
More generally, trying to reduce the differences between your code and the (working) sample will almost always quickly find the problem.
Thanks, VZ, but I found out that it's not the InsertItem() but the SetImageList(). My image list was correct, but the "which" parameter wasn't. Replacing wxIMAGE_LIST_NORMAL by wxIMAGE_LIST_SMALL fixes the problem! I thought "SMALL" was only meant for the SMALL_ICON mode and that "NORMAL" should be the default. But yes, that makes sense, normal icons are big and don't fit in the text display. Would be nice if the documentation had told us that before long trial and error...
This is a simple example for SMALL ICONIC VIEW USING WXLISTCTRL .Please place this code inside the class declaration.I did it in Frame based Windows Application using CODE BLOCKS.It will be useful to you.
wxImageList *il=new wxImageList(32,32,false,0);
wxImageList *i2=new wxImageList(32,32,false,0);
wxDir dir(wxGetCwd());
wxDir dir1(wxGetCwd());
if ( !dir.IsOpened() )
{
// deal with the error here - wxDir would already log an error message
// explaining the exact reason of the failure
return;
}
if ( !dir1.IsOpened() )
{
// deal with the error here - wxDir would already log an error message
// explaining the exact reason of the failure
return;
}
puts("Enumerating object files in current directory:");
wxString path, filename, dirstring,filename1, dirstring1, img,imgPath,path1,img1,imgPath1;
int i=0;
path=wxT("C:\\testing\\splitterwindow\\set\\devices");
path1=wxT("C:\\testing\\splitterwindow\\set\\actions");
img=wxT("C:\\testing\\splitterwindow\\set\\devices\\");
img1=wxT("C:\\testing\\splitterwindow\\set\\actions\\");
bool cont=dir.Open(path);
bool cont1=dir1.Open(path1);
cont = dir.GetFirst(&filename, wxEmptyString, wxDIR_DEFAULT);
dirstring.Append(filename.c_str());
cont1 = dir1.GetFirst(&filename1, wxEmptyString, wxDIR_DEFAULT);
dirstring1.Append(filename1.c_str());
while ( cont )
{
imgPath.clear();
cont = dir.GetNext(&filename);
dirstring.Append(filename.c_str());
// Consturct the imagepath
imgPath.Append(img.c_str());
imgPath.Append(filename.c_str());
//Now, add the images to the imagelist
il->Add(wxBitmap(wxImage(imgPath.c_str())));
i++;
}
while ( cont1 )
{
imgPath1.clear();
cont1 = dir1.GetNext(&filename1);
dirstring1.Append(filename1.c_str());
// Consturct the imagepath
imgPath1.Append(img1.c_str());
imgPath1.Append(filename1.c_str());
//Now, add the images to the imagelist
i2->Add(wxBitmap(wxImage(imgPath1.c_str())));
i++;
}
//assigning the imagelist to listctrl
ListCtrl1->AssignImageList(il, wxIMAGE_LIST_SMALL);
ListCtrl3->AssignImageList(i2, wxIMAGE_LIST_SMALL);
for(int j=0;j < il->GetImageCount()-1;j++)
{
wxListItem itemCol;
itemCol.SetId(j);
itemCol.SetImage(j);
itemCol.SetAlign(wxLIST_FORMAT_LEFT);
ListCtrl1->InsertItem(itemCol);
}
for(int k=0;k < i2->GetImageCount()-1;k++)
{
wxListItem itemCol1;
itemCol1.SetId(k);
itemCol1.SetImage(k);
itemCol1.SetAlign(wxLIST_FORMAT_LEFT);
ListCtrl3->InsertItem(itemCol1);
}
`

Resources