Is there an API for com.apple.TextEncoding? - macos

When you save an NSString (or Swift.String) using a method like this, it writes the xattr "com.apple.TextEncoding". When you load it back with one of the corresponding methods, it checks this xattr and uses that as the default encoding.
Is there any API to determine the encoding of a file, according to this xattr, without having to load the contents of the file?
I know it's not that hard to parse "IANA name, semicolon, CFString​Encoding uint32, (optional other stuff)", but I'd rather avoid it if there's a built-in way.

If I understand your question correctly, you're asking for a way to read the value of the "com.apple.TextEncoding" extended file attribute. This is possible via API declared in <sys/xattr.h>.
Here's a post that extends URL with extended attributes capabilities:
Write extend file attributes swift example
Example usage:
func getTextEncodingAttribute(for url: URL) -> String? {
do {
let data = try url.extendedAttribute(forName: "com.apple.TextEncoding")
return String(data: data, encoding: .utf8)
} catch _ {
}
return nil
}

Related

How to get file as a resource with Storage?

I am trying to lock a file with the flock function but I am not able to do it. I work with Laravel 8 and Storage Class.
The code is as follows:
$disk = Storage::disk('communication');
$file_name = 'received.json';
$file_exists = $disk->exists($file_name);
if($file_exists){
flock($disk->get($file_name), LOCK_EX);
...
}
The problem I'm having is that when I invoke the get() function on the file path, it returns the contents of the file (a string), which causes the following error:
flock() expects parameter 1 to be resource, string given
I need to know how to get file as a resource and not the content of the file.
Could someone help me and tell me how to do it?
Thank you very much in advance.
You can use Storage::readStream() method
if($file_exists){
$stream=Storage::disk('communication')->readStream($file_name);
flock($stream, LOCK_EX);
}
As per php doc
flock(resource $stream, int $operation, int &$would_block = null): bool
First param needed stream.flock() allows you to perform a simple
reader/writer model which can be used on virtually every platform
(including most Unix derivatives and even Windows).
Ref:https://www.php.net/manual/en/function.flock.php

PDF does not use utf-8 string encoding like Go

I am working with libray (https://github.com/unidoc/unipdf) for Go to process PDF files. By using 'SetReason' method I try to set reason of signing of my pdf file.
func (_aggg *PdfSignature )SetReason (reason string ){_aggg .Reason =_gb .MakeString (reason )};
This leads to cyrillic text become unclear symbols (as shown in the picture).
unclear cyricclic symbols
original text is: "русский > Request Id = 12, Task Id = 145"
And it is all ok with cyrillic symbols in main content of PDF file. The problem is in 'Signs'('Подписи') part (as shown in the picture).
In the library there is a mention: (see 'NOTE')
// MakeString creates an PdfObjectString from a string.
// NOTE: **PDF does not use utf-8 string encoding like Go so `s` will often not be a utf-8 encoded
// string.**
func MakeString(s string) *PdfObjectString { _aaad := PdfObjectString{_gcae: s}; return &_aaad }
I want to my pdf file's 'reason' become readable cyrillic symbols,
so, is there any solutions for this ? Hope, I explained the problem ...
It should work if you use core.MakeEncodedString
https://apidocs.unidoc.io/unipdf/latest/github.com/unidoc/unipdf/v3/core/#MakeEncodedString
signature.Reason = core.MakeEncodedString("русский > Request Id = 12, Task Id = 145", true)
func MakeEncodedString(s string, utf16BE bool) *PdfObjectString
MakeEncodedString creates a PdfObjectString with encoded content, which can be either UTF-16BE or PDFDocEncoding depending on whether utf16BE is true or false respectively.
This will store the reason in UTF-16BE which is appropriate for this text.
Disclosure: I am the original developer of UniPDF.

How to save an image in a subdirectory on android Q whilst remaining backwards compatible

I'm creating a simple image editor app and therefore need to load and save image files. I'd like the saved files to appear in the gallery in a separate album. From Android API 28 to 29, there have been drastic changes to what extent an app is able to access storage. I'm able to do what I want in Android Q (API 29) but that way is not backwards compatible.
When I want to achieve the same result in lower API versions, I have so far only found way's, which require the use of deprecated code (as of API 29).
These include:
the use of the MediaStore.Images.Media.DATA column
getting the file path to the external storage via Environment.getExternalStoragePublicDirectory(...)
inserting the image directly via MediaStore.Images.Media.insertImage(...)
My question is: is it possible to implement it in such a way, so it's backwards compatible, but doesn't require deprecated code? If not, is it okay to use deprecated code in this situation or will these methods soon be deleted from the sdk? In any case it feels very bad to use deprecated methods so I'd rather not :)
This is the way I found which works with API 29:
ContentValues values = new ContentValues();
String filename = System.currentTimeMillis() + ".jpg";
values.put(MediaStore.Images.Media.TITLE, filename);
values.put(MediaStore.Images.Media.DISPLAY_NAME, filename);
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
values.put(MediaStore.Images.Media.DATE_ADDED, System.currentTimeMillis() / 1000);
values.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis());
values.put(MediaStore.Images.Media.RELATIVE_PATH, "PATH/TO/ALBUM");
getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,values);
I then use the URI returned by the insert method to save the bitmap. The Problem is that the field RELATIVE_PATH was introduced in API 29 so when I run the code on a lower version, the image is put into the "Pictures" folder and not the "PATH/TO/ALBUM" folder.
is it okay to use deprecated code in this situation or will these methods soon be deleted from the sdk?
The DATA option will not work on Android Q, as that data is not included in query() results, even if you ask for it you cannot use the paths returned by it, even if they get returned.
The Environment.getExternalStoragePublicDirectory(...) option will not work by default on Android Q, though you can add a manifest entry to re-enable it. However, that manifest entry may be removed in Android R, so unless you are short on time, I would not go this route.
AFAIK, MediaStore.Images.Media.insertImage(...) still works, even though it is deprecated.
is it possible to implement it in such a way, so it's backwards compatible, but doesn't require deprecated code?
My guess is that you will need to use two different storage strategies, one for API Level 29+ and one for older devices. I took that approach in this sample app, though there I am working with video content, not images, so insertImage() was not an option.
This is the code that works for me. This code saves an image to a subdirectory folder on your phone. It checks the android version of the phone, if its above android q, it runs the required codes and if its below, it runs the code in the else statement.
Source: https://androidnoon.com/save-file-in-android-10-and-below-using-scoped-storage-in-android-studio/
private void saveImageToStorage(Bitmap bitmap) throws IOException {
OutputStream imageOutStream;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DISPLAY_NAME,
"image_screenshot.jpg");
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
values.put(MediaStore.Images.Media.RELATIVE_PATH,
Environment.DIRECTORY_PICTURES + File.pathSeparator + "AppName");
Uri uri =
getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
values);
imageOutStream = getContentResolver().openOutputStream(uri);
} else {
String imagesDir =
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES). toString() + "/AppName";
File image = new File(imagesDir, "image_screenshot.jpg");
imageOutStream = new FileOutputStream(image);
}
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, imageOutStream);
imageOutStream.close();
}
For old API (<29) I place an image into the external media directory and scan it via MediaScannerConnection.
Let's see my code.
This function creates an image file. Pay attention to an appName variable - it's is a name of an album in which the image will be displayed.
override fun createImageFile(appName: String): File {
val dir = File(appContext.externalMediaDirs[0], appName)
if(!dir.exists()) {
ir.mkdir()
}
return File(dir, createFileName())
}
Then, I place an image into the file, and, at last, I run a media scanner like this:
private suspend fun scanNewFile(shot: File): Uri? {
return suspendCancellableCoroutine { continuation ->
MediaScannerConnection.scanFile(
appContext,
arrayOf<String>(shot.absolutePath),
arrayOf(imageMimeType)) { _, uri -> continuation.resume(uri)
}
}
}
After some trial and error, I discovered that it is possible to use MediaStore in a backwards compatible way, such that as much code as possible is shared between the implementations for different versions. The only trick is to remember that if you use MediaColumns.DATA, you need to create the file yourself.
Let's look at the code from my project (Kotlin). This example is for saving audio, not images, but you only need to substitute MIME_TYPE and DIRECTORY_MUSIC for whatever you require.
private fun newFile(): FileDescriptor? {
// Create a file descriptor for a new recording.
val date = DateFormat.getDateTimeInstance().format(Calendar.getInstance().time)
val filename = "$date.mp3"
val values = ContentValues().apply {
put(MediaColumns.TITLE, date)
put(MediaColumns.MIME_TYPE, "audio/mp3")
// store the file in a subdirectory
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
put(MediaColumns.DISPLAY_NAME, filename)
put(MediaColumns.RELATIVE_PATH, saveTo)
} else {
// RELATIVE_PATH was added in Q, so work around it by using DATA and creating the file manually
#Suppress("DEPRECATION")
val music = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC).path
with(File("$music/P2oggle/$filename")) {
#Suppress("DEPRECATION")
put(MediaColumns.DATA, path)
parentFile!!.mkdir()
createNewFile()
}
}
}
val uri = contentResolver.insert(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, values)!!
return contentResolver.openFileDescriptor(uri, "w")?.fileDescriptor
}
On Android 10 and above, we use DISPLAY_NAME to set the filename and RELATIVE_PATH to set the subdirectory. On older versions, we use DATA and create the file (and its directory) manually. After this, the implementation for both is the same: we simply extract the file descriptor from MediaStore and return it for use.

Zlib::DataError: incorrect header check

I have a string, but I don't know the type of encoding.
Here's what the raw data looks like:
{
"securityProxyResponseEnvelope":{
"resultCode":"OK",
"apiResponse":"{zlibe}9mtdE350h9rd4h7wlFX3AkeCtNsb40FFaEZAl/CfcNNKrhGXawhgAs2rI4rnEgIwLpgJkKl+qkB0kzJ6+ZFmmz12Pl9/9MPdA1unUKL5OdHcWmAZim3ZjDXFGCW4zruCS/IOSiU1qVKAF5qIbocB4+2rAF7zH18SRtmXM8YW3eYs5w1NPjmYkM31W8x7QvrKkzFscH3kqDwmYn0I2gNNOtfwuKjWd5snunyqxPopZHNX3CBdW/pj4+N0tJXjAoHorCe8Ypmjxnvh3zthkLTbiBLgeULH1hGvVtkI0C9PGMyt/92upVW6qHxqCYoO/LTJK1tq6OpBnMRBNZDDntSRkrzp+1RpvzbBxFtwQ9jh45eSthbG5hq+D2oJkW5zrGi6TM8eG4ztCqRoO9dEvz2JbQsDCTPz70+C6iPYdkvOyqji18ysLjBbGcHw1j45YItcurVxp0FChxXrnHZwu6m430xKEp7ONxvgEZurt3T8qAjrkrbHfd8jRjDydUXYsMoa",
"session":"n3qp6jzHwZkXWSMW3VBF:jitqBjBmlZbrgcEgY7Od",
"parameters":{
}
}
}
I want to decompress the string in data['securityProxyResponseEnvelope']['apiResponse'].
Here's what I'm doing:
#clear_string_from_data = '9mtdE350h9rd4h7wlFX3AkeCtNsb40FFaEZAl/CfcNNKrhGXawhgAs2rI4rnEgIwLpgJkKl+qkB0kzJ6+ZFmmz12Pl9/9MPdA1unUKL5OdHcWmAZim3ZjDXFGCW4zruCS/IOSiU1qVKAF5qIbocB4+2rAF7zH18SRtmXM8YW3eYs5w1NPjmYkM31W8x7QvrKkzFscH3kqDwmYn0I2gNNOtfwuKjWd5snunyqxPopZHNX3CBdW/pj4+N0tJXjAoHorCe8Ypmjxnvh3zthkLTbiBLgeULH1hGvVtkI0C9PGMyt/92upVW6qHxqCYoO/LTJK1tq6OpBnMRBNZDDntSRkrzp+1RpvzbBxFtwQ9jh45eSthbG5hq+D2oJkW5zrGi6TM8eG4ztCqRoO9dEvz2JbQsDCTPz70+C6iPYdkvOyqji18ysLjBbGcHw1j45YItcurVxp0FChxXrnHZwu6m430xKEp7ONxvgEZurt3T8qAjrkrbHfd8jRjDydUXYsMoa'
#decoded = Base64.decode64(#clear_string_from_data)
#inflated = Zlib::Inflate.inflate(#decoded)
But this returns
#=> Zlib::DataError: incorrect header check
What's causing this and what could I try next to decompress the data?
What's causing it is that it is not zlib data. You should ask whoever is producing that raw data.
I was getting this when trying to call inflate on a data that hadn't been deflated by Zlib. In my case it was for a unit test and I sent in a plain string and simply forgot to call .deflate on it first.
In your case, if you do this instead you don't get the error:
#decoded = Zlib::Deflate.deflate(#clear_string_from_data)
#inflated = Zlib::Inflate.inflate(#decoded)

NSLocalizedString should be used directly for exporting XLIFF?

I used to use NSLocalizedString by custom function.
For example, to access Profile.strings, I define this function:
func LocalizedProfile(key: String, comment: String?) {
NSLocalizedString(key, tableName: "Profile", comment: comment ?? "")
}
And, called like this:
let localized = LocalizedProfile("Submit", comment: "For registration")
This method works fine except for exporting XLIFF.
On the Xcode 6.3.2, executting Export for localizationthrows error:
To get error information, I executed via command line:
xcodebuild -exportLocalizations -localizationPath ./xliff -project MyApp.xcodeproj -exportLanguage ja
And, I got this error:
Bad entry in file /Users/mono/Documents/Git/MyApp/Localization.swift (line = 29): Argument is not a literal string.
Defining custom localization method is very useful for me, but I also want to use exporting XLIFF feature.
Are there any methods to resolve this demands?
Export For Localization and xcodebuild -exportLocalizations both generate strings files by looking for invocations of NSLocalizedString(_:tableName:bundle:value:comment:) in code and uses the static values passed into the parameters to create the appropriate strings files.
This means that the only values you can pass into key, comment, value, and tableName are string literals.
Since you're using a wrapper function around NSLocalizedString(_:comment:) to localize your strings, the only time Xcode sees you calling NSLocalizedString(_:comment:) is in that one wrapper function with non-string-literal values, which is invalid.
What you really want to do instead is call NSLocalizedString(_:tableName:comment:) directly.
Alternatively, you can call Bundle.localizedString(forKey:value:table:) in your wrapper function, but then you have to manually create your own strings files for those key-value pairs.
/// - parameter comment: This value is ignored, but should be provided for
/// context at the call site about the requested localized string.
func LocalizedProfile(key: String, comment: String?) -> String {
return Bundle.main.localizedString(forKey: key, value: nil, table: "Profile")
}

Resources