How to check if a character is supported by a font - ios

I'm working on an app with a text field. The text wrote in this field will be printed and I have an issue with some characters like emoji, chinese characters, etc... because the font do not provide these characters.
It's why I want to get all the character provided by a font (The font is downloaded so I can deal directly with the file or with an UIFont object).
I heard about CTFontGetGlyphsForCharacters but I'm not sure that this function do what I want and I can't get it work.
Here is my code :
CTFontRef fontRef = CTFontCreateWithName((CFStringRef)font.fontName, font.pointSize, NULL);
NSString *characters = #"🐯"; // emoji character
NSUInteger count = characters.length;
CGGlyph glyphs[count];
if (CTFontGetGlyphsForCharacters(fontRef, (const unichar*)[characters cStringUsingEncoding:NSUTF8StringEncoding], glyphs, count) == false)
NSLog(#"CTFontGetGlyphsForCharacters failed.");
Here CTFontGetGlyphsForCharacters return false. It's what I want because the character '🐯' is not provided by the font used.
The problem is when I replace NSString *characters = #"🐯" by NSString *characters = #"abc", CTFontGetGlyphsForCharacters return again false. Obviously, my font provide a glyph for all the ASCII characters.

I finally solve it :
- (BOOL)isCharacter:(unichar)character supportedByFont:(UIFont *)aFont
{
UniChar characters[] = { character };
CGGlyph glyphs[1] = { };
CTFontRef ctFont = CTFontCreateWithName((CFStringRef)aFont.fontName, aFont.pointSize, NULL);
BOOL ret = CTFontGetGlyphsForCharacters(ctFont, characters, glyphs, 1);
CFRelease(ctFont);
return ret;
}

Related

How to add more strings to the combined string if the length is less than prescribed length?

In iOS I am using two strings to combine and to form a single string. But the combined string must be of 16 characters . So if two strings are small and if we combine both and if it is less than 16 characters I must add few more characters to make it to 16 characters. How to achieve this?
NSString *combined = [NSString stringWithFormat:#"%#%#", stringURL, stringSearch];
This is the code I am using. So if I combine and it is less than 16 characters how to calculate it and add more characters to make it 16 characters?
Something like below
NSString *combined = [NSString stringWithFormat:#"%#%#", stringURL, stringSearch];
if (combined.length < 16)
{
NSString *newCombined = [NSString stringWithFormat:#"%#%#", combined, #"Some new string"];
}
You can use substringWithRange: method from NSString. You can take the below code as an example and modify it as per your requirements.
if (combined.length > 25)
{
NSString *beginning = [combined substringWithRange:NSMakeRange(0, 15)];
NSString *fromEnd = [combined substringWithRange:NSMakeRange(startPoint, combined.length-startPoint)];
}
You could make use of stringWithFormat - basically of printf if you want to pad with just a single char. Below I give some examples which I have constructed to illustrate, so it won't run out the box, but you only need to comment out the ones you do not want to make it work.
// To get 50 spaces
NSString * s50 = [NSString stringWithFormat:#"%*s", 50, ""];
// Pad with these characters, select only 1
// This will pad with spaces
char * pad = "";
// This will pad with minuses - you need enough to fill the whole field
char * pad = "-------------------------------------------------------";
// Some string
NSString * s = #"Hi there";
// Here back and front are just int's. They must be, but they can be calculated,
// e.g. you could have this to pad to 50
int back = 50 - s.length; if ( back < 0 ) back = 0;
// Pad s at the back
int back = 20;
NSString * sBack = [NSString stringWithFormat:#"%#%*s", s, back, pad];
// Pad s in front
int front = 10;
NSString * sFront = [NSString stringWithFormat:#"%*s%#", front, pad, s];
// Pad s both sides
NSString * sBoth = [NSString stringWithFormat:#"%*s%#%*s", front, pad, s, back, pad];
Note that the amounts here are parameterised. I use e.g. 50 in the first line but that could just as well be n as long as n is an int and you can use that to then perform calculations, store it in n and pad. There is an example in the code.
Here is a sample of the output
2020-11-04 08:16:22.908828+0200 FormatSpecifiers[768:15293] [Hi there-------------------------------------------------------]
2020-11-04 08:16:22.908931+0200 FormatSpecifiers[768:15293] [-------------------------------------------------------Hi there]
2020-11-04 08:16:22.908992+0200 FormatSpecifiers[768:15293] [-------------------------------------------------------Hi there-------------------------------------------------------]
I just show how to pad the combined string. To combine the string of course just use stringByAppendingString e.g.
NSString * s = [a stringByAppendingString:b];
and then you can do calcs based on s.length e.g. as shown in the example.

How to detect emoji iOS

I am trying to detect if a user of my app entered an emoji into UITextView. I have found this code:
https://gist.github.com/cihancimen/4146056
However this code is not working for all emojis (for instance it is not working for the hearth symbol). Does anyone have a clue how to improve the code to catch all emojis? I am using Objective-C language. Any help is appreciated.
This is how I do it in my app :
func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
if textView.textInputMode?.primaryLanguage == "emoji" || textView.textInputMode?.primaryLanguage == nil {
// An emoji was typed by the user
// Do anything you need to do (or return false to disallow emojis)
}
return true
}
If you need to be able to detect any emoji, you'll need to create a list containing all code points used for emoji (or a list of all emoji if you prefer). If you want to, you can look at how emoji are detected in this framework, which I created for the purpose of replacing standard emoji with custom images, or take a look at my answer to a related question.
Then, if you're working with Objective-C and the NSString type, you'll first have to convert the string's unichars (which are UTF-16 encoded) into UTF-32 compatible format in order to use your list of code points. When you have the UTF-32 value, just compare it against your list and handle it however you need:
// Sample text.
NSString *text = #"a 😁";
// Get the UTF-16 representation of the text.
unsigned long length = text.length;
unichar buffer[length];
[text getCharacters:buffer];
// Initialize array to hold our UTF-32 values.
NSMutableArray *array = [[NSMutableArray alloc] init];
// Temporary stores for the UTF-32 and UTF-16 values.
UTF32Char utf32 = 0;
UTF16Char h16 = 0, l16 = 0;
for (int i = 0; i < length; i++) {
unichar surrogate = buffer[i];
// High surrogate.
if (0xd800 <= surrogate && surrogate <= 0xd83f) {
h16 = surrogate;
continue;
}
// Low surrogate.
else if (0xdc00 <= surrogate && surrogate <= 0xdfff) {
l16 = surrogate;
// Convert surrogate pair to UTF-32 encoding.
utf32 = ((h16 - 0xd800) << 10) + (l16 - 0xdc00) + 0x10000;
}
// Normal UTF-16.
else {
utf32 = surrogate;
}
// Compare the UTF-32 value against your list of code points, and handle.
// Just demonstrating with the code point for 😁.
if (utf32 == 0x1f601) {
NSLog(#"It's an emoji!");
}
}
Additionally, you'll need to handle Variation Selectors if you don't want false positives, and zero-width joiners if you need to be able to handle sequences, but just looking at the first character in a sequence will tell you whether the string contains an emoji, so I won't go further into this.

NSAttributedString: Setting FontAttributes doesn't change font

I am trying to change the font on an NSAttributedString.
I have a method which iterates over each character, and creates a new NSFontAttribute dictionary for that character. In the end, however, the string remains un-changed. To demonstrate, here is how I setup the string:
UIFontDescriptor *fontDescriptor = [UIFontDescriptor fontDescriptorWithName:#"Avenir-Book" size:14.0f];
NSDictionary *fontAttributes = [fontDescriptor fontAttributes];
[fontAttributes setValue:[NSString stringWithFormat:#"%u",[fontDescriptor symbolicTraits]] forKey:UIFontSymbolicTrait];
[mutableAttributedString setAttributes:fontAttributes range:(NSRange){0,length}];
This produces the following NSFontAttribute dictionary for the entire string:
Font Attributes: {
NSCTFontSymbolicTrait = 2147483648;
NSFontNameAttribute = "Avenir-Book";
NSFontSizeAttribute = 14;
}
I go through and modify each character's FontAttribute to add bolding or italics, as follows:
for (int i = (int)range.location; i < (range.location + range.length); i++){
/* Extract Font Attributes */
NSDictionary *extractedAttributes = [[mutableAttributedString attributesAtIndex:i effectiveRange:NULL]mutableCopy];
/* Determine New Trait */
uint newTrait = ((uint)[[extractedAttributes valueForKey:UIFontSymbolicTrait]longLongValue] | [self symbolicTraitForMarkdownType:markdown]); // (markDown is a mask)
/* Set New Trait */
[extractedAttributes setValue:[NSString stringWithFormat:#"%u",newTrait] forKey:UIFontSymbolicTrait];
/* Create New Font Descriptor */
UIFontDescriptor *newDescriptor = [UIFontDescriptor fontDescriptorWithFontAttributes:extractedAttributes];
newDescriptor = [newDescriptor fontDescriptorWithSymbolicTraits:newTrait];
/* Apply Font Descriptor */
[mutableAttributedString setAttributes:[newDescriptor fontAttributes] range:(NSRange){i,1}];
}
This produces many different FontAttributes:
Index 1: {
NSCTFontSymbolicTrait = 2147483650;
NSFontNameAttribute = "Avenir-Black";
NSFontSizeAttribute = 14;
}
Index 2: {
NSCTFontSymbolicTrait = 2147483651;
NSFontNameAttribute = "Avenir-BlackOblique";
NSFontSizeAttribute = 14;
}
However, the NSAttributedString itself remains completely un-changed. It is still the default Font. How can I get it to reflect the changes I am making to its attributes?
Here is the solution:
1: The documentation for UIFontDescriptor defines the key: UIFontDescriptorNameAttribute as an NSString instance, which it is.
2: The documentation for NSAttributedString defines the key: NSFontAttributeName as a UIFont instance.
So obtaining the fontAttributes dictionary from a UIFontDescriptor initialized with a the method: (UIFontDescriptor *)fontDescriptorWithName:(NSString *)fontName size:(CGFloat)size will only set the keys UIFontDescriptorNameAttribute and UIFontDescriptorSizeAttribute. If you wish to actually modify the font of an NSAttributedString however, you need to apply attributes with a UIFont instance saved to the key NSFontAttributeName.
This is where the confusion comes from. However, it should be noted that you can actually obtain UIFontDescriptorNameAttribute from the fontAttributes dictionary of a UIFontDescriptor instance with the key NSFontAttributeName. This may also be confusing.

Allowing special characters in iOS

I am allowing the user to input some data into the TextField. The user inputs Š1234D into the TextField.
The code I have looks like this:
NSString *string = textField.text;
for (int nCtr = 0; nCtr < [string length]; nCtr++) {
const char chars = [string characterAtIndex:nCtr];
int isAlpha = isalpha(chars);
}
string output looks like this:Š1234D
Then I printed the first chars value, it looks like this:'`' instead of 'Š'. Why is this so? I would like to allow special characters in my code as well.
Any suggestion would be welcome as well. Need some guidance. Thanks
You are truncating the character value as [NSString chatacterAtIndex:] returns unichar (16-bit) and not char (8-bit). try:
unichar chars = [string characterAtIndex:nCtr];
UPDATE: Also note that you shouldn't be using isalpha() to test for letters, as that is restricted to Latin character sets and you need something that can cope with non-latin characters. Use this code instead:
NSCharacterSet *letterSet = [NSCharacterSet letterCharacterSet];
NSString *string = textField.text;
for (NSUIntger nCtr = 0; nCtr < [string length]; nCtr++)
{
const unichar c = [string characterAtIndex:nCtr];
BOOL isAlpha = [letterSet characterIsMember:c];
...
}
characterAtIndex: returns a unichar (2-byte Unicode character), not char (1-byte ASCII character). By casting it to char, you are getting only one of the two bytes.
You should turn on your compiler warnings. I believe "Suspicious implicit conversions" should do the trick.
On a separate note, you can't use isAlpha(char) with a unichar. Use [[NSCharacterSet letterCharacterSet] characterIsMember:chars]

how to read chinese from pdf in ios correctly

here is what I have done, but it appears disorderly. Thanks in advance.
1.use CGPDFStringCopyTextString to get the text from the pdf
2.encode the NSString to char*
NSStringEncoding enc = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
const char *char_content = [self.currentData cStringUsingEncoding:enc];
Below is how I get the currentData:
void arrayCallback(CGPDFScannerRef inScanner, void *userInfo)
{
BIDViewController *pp = (__bridge BIDViewController*)userInfo;
CGPDFArrayRef array;
bool success = CGPDFScannerPopArray(inScanner, &array);
for(size_t n = 0; n < CGPDFArrayGetCount(array); n += 1)
{
if(n >= CGPDFArrayGetCount(array))
continue;
CGPDFStringRef string;
success = CGPDFArrayGetString(array, n, &string);
if(success)
{
NSString *data = (__bridge NSString *)CGPDFStringCopyTextString(string);
[pp.currentData appendFormat:#"%#", data];
}
}
}
- (IBAction)press:(id)sender {
table = CGPDFOperatorTableCreate();
CGPDFOperatorTableSetCallback(table, "TJ", arrayCallback);
CGPDFOperatorTableSetCallback(table, "Tj", stringCallback);
self.currentData = [NSMutableString string];
CGPDFContentStreamRef contentStream = CGPDFContentStreamCreateWithPage(pagerf);
CGPDFScannerRef scanner = CGPDFScannerCreate(contentStream, table, (__bridge void *)(self));
bool ret = CGPDFScannerScan(scanner);
}
According to the Mac Developer Library
CGPDFStringCopyTextString returns a CFString object that represents a PDF string as a text string. The PDF string is given as a CGPDFString which is a series of bytes—unsigned integer values in the range 0 to 255; thus, this method already decodes the bytes according to some character encoding.
It is given none explicitly, so it assumes one encoding type, most likely the PDFDocEncoding or the UTF-16BE Unicode character encoding scheme which are the two encodings that may be used to represent text strings in a PDF document outside the document’s content streams, cf. section 7.9.2.2 Text String Type and Table D.1, Annex D in the PDF specification.
Now you have not told us from where you received your CGPDFString. I assume, though, that you received it from inside one of the document’s content streams. Text strings there, on the other hand, can be encoded with any imaginable encoding. The encoding used is given by the embedded data of the font the string is to be displayed with.
For more information on this you may want to read CGPDFScannerPopString returning strange result and have a look at PDFKitten.

Resources