Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to use local (from assets folder) sprites and glyphs #338

Open
venomwine opened this issue Nov 27, 2023 · 3 comments
Open

How to use local (from assets folder) sprites and glyphs #338

venomwine opened this issue Nov 27, 2023 · 3 comments
Labels
documentation Improvements or additions to documentation

Comments

@venomwine
Copy link

Good library!

When I use maplibre android, I can use local (from assets folder) sprites and glyphs.
The keywords of style.json are just like this.

"sprite": "asset://sprites/sprite",
"glyphs": "asset://glyphs/{fontstack}/{range}.pbf",

and all files are under assets folder.
image

But with this flutter-maplibre-gl, That way is not working.
I try to copy those files to under flutter assets folder and android assets folder. Both way couldn't load asset.
image

Is there any way to use local sprites and glyphs ??

Thanks!!

@m0nac0
Copy link
Collaborator

m0nac0 commented Nov 27, 2023

Sorry, the documentation isn't very good about that, and we should investigate approaches and improve the documentation.

One approach that has come up a few times is copying the assets from the asset directory to the file system (in flutter) and then reference that file system location (see e.g. #318).

More as a reference for contributors: we may also be able to add a lookup function in the native code of this library (ref.: e.g. https://stackoverflow.com/questions/56339637/is-there-a-way-to-access-flutter-resources-from-native-code).

@m0nac0 m0nac0 added the documentation Improvements or additions to documentation label Nov 27, 2023
@venomwine
Copy link
Author

The "asset" keyword is not working.

But "file" keyword is working.
"glyphs": "file://appCacheDir/glyphs/{fontstack}/{range}.pbf"

Thanks!

m0nac0 added a commit that referenced this issue Dec 1, 2023
JulianBissekkou added a commit that referenced this issue Dec 19, 2023
…tc.) (#346)

This came up e.g. in
#338 and
#318

Co-authored-by: Julian Bissekkou <36447137+JulianBissekkou@users.noreply.github.com>
@mariusvn
Copy link
Contributor

mariusvn commented Jan 24, 2024

To help the people that want to use this in the future, the venomwine answer only includes the final step but you first have to move the glyphs to the cache directory.

Here is the functions I use in my project:

class GlyphsService {
  /// Copies the glyphs files to cache dir to allow native code to access it
  Future<void> copyGlyphsToCacheDir() async {
    var dir = (await getApplicationCacheDirectory()).path;

    /* Check if the files are present */

    var directory = Directory(dir);

    var glyphFilesInDirectory = directory.listSync(recursive: true).where((file) => p.basename(file.path).startsWith('glyph'))
        .map((file) => p.basename(file.path));

    if (glyphFilesInDirectory.isNotEmpty) {
      print('Glyph files directories are already present');
      // Exits here if glyph files already exist
      return;
    }

    /* If not present, copy */

    final List<String> glyphsAssets = await _getGlyphAssets();
    final int glyphAmount = glyphsAssets.length;
    for (var i = 0; i < glyphAmount; i++) {
      final String asset = glyphsAssets[i];
      final String assetPath = p.dirname(asset);
      final String assetDir = p.join(dir, assetPath);
      final String assetFileName = p.basename(asset);

      // Create the directory structure if it's not present
      await Directory(assetDir).create(recursive: true);

      final ByteData data = await rootBundle.load(asset);
      final String path = p.join(assetDir, assetFileName);
      await _writeAssetToFile(data, path);
      print('[${i+1}/$glyphAmount] "$asset" copied to "$path".');
    }
  }

  Future<List<String>> _getGlyphAssets() {
    return rootBundle
        .loadString('AssetManifest.json')
        .then<List<String>>((String manifestJson) {
      Map<String, dynamic> manifestMap = jsonDecode(manifestJson);
      return manifestMap.keys
          .where((String key) => key.contains('assets/glyphs'))
          .toList();
    });
  }

  Future<void> _writeAssetToFile(ByteData data, String path) {
    final buffer = data.buffer;
    return File(path).writeAsBytes(
        buffer.asUint8List(data.offsetInBytes, data.lengthInBytes));
  }
}

This code assumes you have the glyphs in the app's directory assets/glyphs like this:

idea64_3MSL8cARDJ

Once the assets files are there, get the paths to the pubspec:

flutter:
  assets:
    - assets/
    - assets/glyphs/Noto Sans Bold/
    - assets/glyphs/Noto Sans Italic/
    - assets/glyphs/Noto Sans Regular/
    - assets/glyphs/Open Sans Regular,Arial Unicode MS Regular/
    - assets/glyphs/Open Sans Semibold/

If you call copyGlyphsToCacheDir it will check if the files are present in the cache dir and if this is not the case, it will copy to yourAppCacheDir/assets/glyphs keeping the directory strucure.

I recommend to call this function when the app is starting.

Once you are done calling this function, you should use a style with this glyph value:

final String cacheDirPath = (await getApplicationCacheDirectory()).path;

// Then, the style should look like this (at least, the glyphs value)

final String style = '''{
  "version": 8,
  "glyphs": "file://$cacheDirPath/assets/glyphs/{fontstack}/{range}.pbf",
  "sources": {},
  "layers": []
}''';

Now this should work with no download :)

EDIT: not that the prints shoud be only on debug. I used a custom logger on my project that does it somewhere else so thats why they are not if-ed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation
Projects
None yet
Development

No branches or pull requests

3 participants