expo-font 58.0.0 → 58.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (254) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/android/build.gradle +2 -2
  3. package/android/src/main/java/expo/modules/font/FontFamilyFaces.kt +66 -0
  4. package/android/src/main/java/expo/modules/font/FontLoaderModule.kt +47 -13
  5. package/android/src/main/java/expo/modules/font/FontLoaderRecords.kt +17 -0
  6. package/android/src/main/java/expo/modules/font/FontSource.kt +13 -0
  7. package/android/src/main/java/expo/modules/font/MultiFaceTypeface.kt +130 -0
  8. package/android/src/main/java/expo/modules/font/VariableTypefaces.kt +5 -2
  9. package/build/ExpoFontLoader.d.ts +6 -0
  10. package/build/ExpoFontLoader.js +3 -0
  11. package/build/ExpoFontLoader.js.map +1 -1
  12. package/build/ExpoFontLoader.web.d.ts +5 -2
  13. package/build/ExpoFontLoader.web.js +110 -29
  14. package/build/ExpoFontLoader.web.js.map +1 -1
  15. package/build/ExpoFontUtils.web.d.ts +1 -1
  16. package/build/Font.d.ts +9 -5
  17. package/build/Font.js +54 -6
  18. package/build/Font.js.map +1 -1
  19. package/build/Font.types.d.ts +80 -4
  20. package/build/Font.types.js.map +1 -1
  21. package/build/FontHooks.d.ts +22 -2
  22. package/build/FontHooks.js +27 -2
  23. package/build/FontHooks.js.map +1 -1
  24. package/build/FontLoader.d.ts +4 -1
  25. package/build/FontLoader.js +32 -1
  26. package/build/FontLoader.js.map +1 -1
  27. package/build/FontLoader.web.d.ts +2 -1
  28. package/build/FontLoader.web.js +45 -22
  29. package/build/FontLoader.web.js.map +1 -1
  30. package/build/fontFaceValidation.d.ts +7 -0
  31. package/build/fontFaceValidation.js +86 -0
  32. package/build/fontFaceValidation.js.map +1 -0
  33. package/build/fontSourceFromFace.d.ts +2 -0
  34. package/build/fontSourceFromFace.js +32 -0
  35. package/build/fontSourceFromFace.js.map +1 -0
  36. package/build/serverContext.web.js +4 -2
  37. package/build/serverContext.web.js.map +1 -1
  38. package/expo-module.config.json +1 -1
  39. package/ios/FontExceptions.swift +14 -0
  40. package/ios/FontFamilyAliasManager.swift +56 -23
  41. package/ios/FontLoaderModule.swift +128 -5
  42. package/ios/FontLoaderRecords.swift +10 -0
  43. package/ios/FontUtils.swift +48 -5
  44. package/ios/UIFont+FontFamilyAlias.swift +19 -17
  45. package/local-maven-repo/host/exp/exponent/expo.modules.font/58.0.2/expo.modules.font-58.0.2-sources.jar +0 -0
  46. package/local-maven-repo/host/exp/exponent/expo.modules.font/58.0.2/expo.modules.font-58.0.2-sources.jar.md5 +1 -0
  47. package/local-maven-repo/host/exp/exponent/expo.modules.font/58.0.2/expo.modules.font-58.0.2-sources.jar.sha1 +1 -0
  48. package/local-maven-repo/host/exp/exponent/expo.modules.font/58.0.2/expo.modules.font-58.0.2-sources.jar.sha256 +1 -0
  49. package/local-maven-repo/host/exp/exponent/expo.modules.font/58.0.2/expo.modules.font-58.0.2-sources.jar.sha512 +1 -0
  50. package/local-maven-repo/host/exp/exponent/expo.modules.font/58.0.2/expo.modules.font-58.0.2.aar +0 -0
  51. package/local-maven-repo/host/exp/exponent/expo.modules.font/58.0.2/expo.modules.font-58.0.2.aar.md5 +1 -0
  52. package/local-maven-repo/host/exp/exponent/expo.modules.font/58.0.2/expo.modules.font-58.0.2.aar.sha1 +1 -0
  53. package/local-maven-repo/host/exp/exponent/expo.modules.font/58.0.2/expo.modules.font-58.0.2.aar.sha256 +1 -0
  54. package/local-maven-repo/host/exp/exponent/expo.modules.font/58.0.2/expo.modules.font-58.0.2.aar.sha512 +1 -0
  55. package/{android/build/publications/release/module.json → local-maven-repo/host/exp/exponent/expo.modules.font/58.0.2/expo.modules.font-58.0.2.module} +22 -22
  56. package/local-maven-repo/host/exp/exponent/expo.modules.font/58.0.2/expo.modules.font-58.0.2.module.md5 +1 -0
  57. package/local-maven-repo/host/exp/exponent/expo.modules.font/58.0.2/expo.modules.font-58.0.2.module.sha1 +1 -0
  58. package/local-maven-repo/host/exp/exponent/expo.modules.font/58.0.2/expo.modules.font-58.0.2.module.sha256 +1 -0
  59. package/local-maven-repo/host/exp/exponent/expo.modules.font/58.0.2/expo.modules.font-58.0.2.module.sha512 +1 -0
  60. package/{android/build/publications/release/pom-default.xml → local-maven-repo/host/exp/exponent/expo.modules.font/58.0.2/expo.modules.font-58.0.2.pom} +1 -1
  61. package/local-maven-repo/host/exp/exponent/expo.modules.font/58.0.2/expo.modules.font-58.0.2.pom.md5 +1 -0
  62. package/local-maven-repo/host/exp/exponent/expo.modules.font/58.0.2/expo.modules.font-58.0.2.pom.sha1 +1 -0
  63. package/local-maven-repo/host/exp/exponent/expo.modules.font/58.0.2/expo.modules.font-58.0.2.pom.sha256 +1 -0
  64. package/local-maven-repo/host/exp/exponent/expo.modules.font/58.0.2/expo.modules.font-58.0.2.pom.sha512 +1 -0
  65. package/local-maven-repo/host/exp/exponent/expo.modules.font/maven-metadata.xml +4 -4
  66. package/local-maven-repo/host/exp/exponent/expo.modules.font/maven-metadata.xml.md5 +1 -1
  67. package/local-maven-repo/host/exp/exponent/expo.modules.font/maven-metadata.xml.sha1 +1 -1
  68. package/local-maven-repo/host/exp/exponent/expo.modules.font/maven-metadata.xml.sha256 +1 -1
  69. package/local-maven-repo/host/exp/exponent/expo.modules.font/maven-metadata.xml.sha512 +1 -1
  70. package/mocks/ExpoFontLoader.ts +6 -0
  71. package/package.json +3 -7
  72. package/prebuilds/output/debug/xcframeworks/ExpoFont.tar.gz +0 -0
  73. package/prebuilds/output/release/xcframeworks/ExpoFont.tar.gz +0 -0
  74. package/src/ExpoFontLoader.ts +11 -0
  75. package/src/ExpoFontLoader.web.ts +152 -39
  76. package/src/Font.ts +75 -12
  77. package/src/Font.types.ts +86 -4
  78. package/src/FontHooks.ts +31 -6
  79. package/src/FontLoader.ts +51 -6
  80. package/src/FontLoader.web.ts +61 -26
  81. package/src/fontFaceValidation.ts +124 -0
  82. package/src/fontSourceFromFace.ts +29 -0
  83. package/src/serverContext.web.ts +1 -1
  84. package/.npmignore +0 -19
  85. package/.tsbuildinfo +0 -1
  86. package/android/.gitignore +0 -261
  87. package/android/build/intermediates/aapt_friendly_merged_manifests/release/processReleaseManifest/aapt/AndroidManifest.xml +0 -7
  88. package/android/build/intermediates/aapt_friendly_merged_manifests/release/processReleaseManifest/aapt/output-metadata.json +0 -18
  89. package/android/build/intermediates/aar_main_jar/release/syncReleaseLibJars/classes.jar +0 -0
  90. package/android/build/intermediates/aar_metadata/release/writeReleaseAarMetadata/aar-metadata.properties +0 -7
  91. package/android/build/intermediates/annotation_processor_list/release/javaPreCompileRelease/annotationProcessors.json +0 -1
  92. package/android/build/intermediates/annotations_typedef_file/release/extractReleaseAnnotations/typedefs.txt +0 -0
  93. package/android/build/intermediates/compile_r_class_jar/release/generateReleaseRFile/R.jar +0 -0
  94. package/android/build/intermediates/compile_symbol_list/release/generateReleaseRFile/R.txt +0 -0
  95. package/android/build/intermediates/incremental/mergeReleaseAssets/merger.xml +0 -2
  96. package/android/build/intermediates/incremental/mergeReleaseJniLibFolders/merger.xml +0 -2
  97. package/android/build/intermediates/incremental/release/packageReleaseResources/compile-file-map.properties +0 -1
  98. package/android/build/intermediates/incremental/release/packageReleaseResources/merger.xml +0 -2
  99. package/android/build/intermediates/incremental/release-mergeJavaRes/merge-state +0 -0
  100. package/android/build/intermediates/java_res/release/processReleaseJavaRes/out/META-INF/expo-font_release.kotlin_module +0 -0
  101. package/android/build/intermediates/local_only_symbol_list/release/parseReleaseLocalResources/R-def.txt +0 -2
  102. package/android/build/intermediates/manifest_merge_blame_file/release/processReleaseManifest/manifest-merger-blame-release-report.txt +0 -9
  103. package/android/build/intermediates/merged_java_res/release/mergeReleaseJavaResource/feature-expo-font.jar +0 -0
  104. package/android/build/intermediates/merged_manifest/release/processReleaseManifest/AndroidManifest.xml +0 -7
  105. package/android/build/intermediates/nested_resources_validation_report/release/generateReleaseResources/nestedResourcesValidationReport.txt +0 -1
  106. package/android/build/intermediates/source_jar/release/release-sources.jar +0 -0
  107. package/android/build/intermediates/symbol_list_with_package_name/release/generateReleaseRFile/package-aware-r.txt +0 -1
  108. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/inputs/source-to-output.tab +0 -0
  109. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.keystream +0 -0
  110. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.keystream.len +0 -0
  111. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.len +0 -0
  112. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.values.at +0 -0
  113. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/inputs/source-to-output.tab_i +0 -0
  114. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/inputs/source-to-output.tab_i.len +0 -0
  115. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab +0 -0
  116. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.keystream +0 -0
  117. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.keystream.len +0 -0
  118. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.len +0 -0
  119. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.values.at +0 -0
  120. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab_i +0 -0
  121. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab_i.len +0 -0
  122. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab +0 -0
  123. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream +0 -0
  124. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream.len +0 -0
  125. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.len +0 -0
  126. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.values.at +0 -0
  127. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab_i +0 -0
  128. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab_i.len +0 -0
  129. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab +0 -0
  130. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab.keystream +0 -0
  131. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab.keystream.len +0 -0
  132. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab.len +0 -0
  133. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab.values.at +0 -0
  134. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab_i +0 -0
  135. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/constants.tab_i.len +0 -0
  136. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab +0 -0
  137. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream +0 -0
  138. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream.len +0 -0
  139. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.len +0 -0
  140. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.values.at +0 -0
  141. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i +0 -0
  142. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i.len +0 -0
  143. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/package-parts.tab +0 -0
  144. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/package-parts.tab.keystream +0 -0
  145. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/package-parts.tab.keystream.len +0 -0
  146. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/package-parts.tab.len +0 -0
  147. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/package-parts.tab.values.at +0 -0
  148. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/package-parts.tab_i +0 -0
  149. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/package-parts.tab_i.len +0 -0
  150. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab +0 -0
  151. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.keystream +0 -0
  152. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.keystream.len +0 -0
  153. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.len +0 -0
  154. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.values.at +0 -0
  155. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab_i +0 -0
  156. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab_i.len +0 -0
  157. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab +0 -0
  158. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.keystream +0 -0
  159. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.keystream.len +0 -0
  160. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.len +0 -0
  161. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.values.at +0 -0
  162. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab_i +0 -0
  163. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab_i.len +0 -0
  164. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab +0 -0
  165. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.keystream +0 -0
  166. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.keystream.len +0 -0
  167. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.len +0 -0
  168. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.values.at +0 -0
  169. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab_i +0 -0
  170. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab_i.len +0 -0
  171. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab +0 -0
  172. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.keystream +0 -0
  173. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.keystream.len +0 -0
  174. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.len +0 -0
  175. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.values.at +0 -0
  176. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab_i +0 -0
  177. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab_i.len +0 -0
  178. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/lookups/counters.tab +0 -2
  179. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/lookups/file-to-id.tab +0 -0
  180. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.keystream +0 -0
  181. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.keystream.len +0 -0
  182. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.len +0 -0
  183. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.values.at +0 -0
  184. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/lookups/file-to-id.tab_i +0 -0
  185. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/lookups/file-to-id.tab_i.len +0 -0
  186. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/lookups/id-to-file.tab +0 -0
  187. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.keystream +0 -0
  188. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.keystream.len +0 -0
  189. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.len +0 -0
  190. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.values.at +0 -0
  191. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/lookups/id-to-file.tab_i +0 -0
  192. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/lookups/id-to-file.tab_i.len +0 -0
  193. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/lookups/lookups.tab +0 -0
  194. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream +0 -0
  195. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream.len +0 -0
  196. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/lookups/lookups.tab.len +0 -0
  197. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/lookups/lookups.tab.values.at +0 -0
  198. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/lookups/lookups.tab_i +0 -0
  199. package/android/build/kotlin/compileReleaseKotlin/cacheable/caches-jvm/lookups/lookups.tab_i.len +0 -0
  200. package/android/build/kotlin/compileReleaseKotlin/cacheable/last-build.bin +0 -0
  201. package/android/build/kotlin/compileReleaseKotlin/classpath-snapshot/shrunk-classpath-snapshot.bin +0 -0
  202. package/android/build/outputs/aar/expo-font-release.aar +0 -0
  203. package/android/build/outputs/logs/manifest-merger-release-report.txt +0 -14
  204. package/android/build/tmp/kotlin-classes/release/META-INF/expo-font_release.kotlin_module +0 -0
  205. package/android/build/tmp/kotlin-classes/release/expo/modules/font/FileNotFoundException.class +0 -0
  206. package/android/build/tmp/kotlin-classes/release/expo/modules/font/FontBytesKt.class +0 -0
  207. package/android/build/tmp/kotlin-classes/release/expo/modules/font/FontLoaderModule$definition$lambda$4$$inlined$AsyncFunction$1.class +0 -0
  208. package/android/build/tmp/kotlin-classes/release/expo/modules/font/FontLoaderModule$definition$lambda$4$$inlined$AsyncFunction$2.class +0 -0
  209. package/android/build/tmp/kotlin-classes/release/expo/modules/font/FontLoaderModule$definition$lambda$4$$inlined$AsyncFunction$3.class +0 -0
  210. package/android/build/tmp/kotlin-classes/release/expo/modules/font/FontLoaderModule$definition$lambda$4$$inlined$FunctionWithoutArgs$1.class +0 -0
  211. package/android/build/tmp/kotlin-classes/release/expo/modules/font/FontLoaderModule.class +0 -0
  212. package/android/build/tmp/kotlin-classes/release/expo/modules/font/FontLoaderModuleKt.class +0 -0
  213. package/android/build/tmp/kotlin-classes/release/expo/modules/font/FontSource$Asset.class +0 -0
  214. package/android/build/tmp/kotlin-classes/release/expo/modules/font/FontSource$Companion.class +0 -0
  215. package/android/build/tmp/kotlin-classes/release/expo/modules/font/FontSource$LocalFile.class +0 -0
  216. package/android/build/tmp/kotlin-classes/release/expo/modules/font/FontSource.class +0 -0
  217. package/android/build/tmp/kotlin-classes/release/expo/modules/font/FontSourceKt.class +0 -0
  218. package/android/build/tmp/kotlin-classes/release/expo/modules/font/FontUtilsModule$definition$lambda$5$$inlined$AsyncFunctionWithPromise$1.class +0 -0
  219. package/android/build/tmp/kotlin-classes/release/expo/modules/font/FontUtilsModule$definition$lambda$5$$inlined$AsyncFunctionWithPromise$2.class +0 -0
  220. package/android/build/tmp/kotlin-classes/release/expo/modules/font/FontUtilsModule$definition$lambda$5$$inlined$AsyncFunctionWithPromise$3.class +0 -0
  221. package/android/build/tmp/kotlin-classes/release/expo/modules/font/FontUtilsModule.class +0 -0
  222. package/android/build/tmp/kotlin-classes/release/expo/modules/font/FontVariationAxes.class +0 -0
  223. package/android/build/tmp/kotlin-classes/release/expo/modules/font/RenderToImageOptions$__Pika.class +0 -0
  224. package/android/build/tmp/kotlin-classes/release/expo/modules/font/RenderToImageOptions.class +0 -0
  225. package/android/build/tmp/kotlin-classes/release/expo/modules/font/SaveImageException.class +0 -0
  226. package/android/build/tmp/kotlin-classes/release/expo/modules/font/VariableTypefaces.class +0 -0
  227. package/android/build/tmp/publishReleasePublicationToNPMPackageRepository/module-maven-metadata.xml +0 -13
  228. package/android/build/tmp/sourceReleaseJar/MANIFEST.MF +0 -2
  229. package/jest.config.js +0 -3
  230. package/local-maven-repo/host/exp/exponent/expo.modules.font/58.0.0/expo.modules.font-58.0.0-sources.jar +0 -0
  231. package/local-maven-repo/host/exp/exponent/expo.modules.font/58.0.0/expo.modules.font-58.0.0-sources.jar.md5 +0 -1
  232. package/local-maven-repo/host/exp/exponent/expo.modules.font/58.0.0/expo.modules.font-58.0.0-sources.jar.sha1 +0 -1
  233. package/local-maven-repo/host/exp/exponent/expo.modules.font/58.0.0/expo.modules.font-58.0.0-sources.jar.sha256 +0 -1
  234. package/local-maven-repo/host/exp/exponent/expo.modules.font/58.0.0/expo.modules.font-58.0.0-sources.jar.sha512 +0 -1
  235. package/local-maven-repo/host/exp/exponent/expo.modules.font/58.0.0/expo.modules.font-58.0.0.aar +0 -0
  236. package/local-maven-repo/host/exp/exponent/expo.modules.font/58.0.0/expo.modules.font-58.0.0.aar.md5 +0 -1
  237. package/local-maven-repo/host/exp/exponent/expo.modules.font/58.0.0/expo.modules.font-58.0.0.aar.sha1 +0 -1
  238. package/local-maven-repo/host/exp/exponent/expo.modules.font/58.0.0/expo.modules.font-58.0.0.aar.sha256 +0 -1
  239. package/local-maven-repo/host/exp/exponent/expo.modules.font/58.0.0/expo.modules.font-58.0.0.aar.sha512 +0 -1
  240. package/local-maven-repo/host/exp/exponent/expo.modules.font/58.0.0/expo.modules.font-58.0.0.module +0 -105
  241. package/local-maven-repo/host/exp/exponent/expo.modules.font/58.0.0/expo.modules.font-58.0.0.module.md5 +0 -1
  242. package/local-maven-repo/host/exp/exponent/expo.modules.font/58.0.0/expo.modules.font-58.0.0.module.sha1 +0 -1
  243. package/local-maven-repo/host/exp/exponent/expo.modules.font/58.0.0/expo.modules.font-58.0.0.module.sha256 +0 -1
  244. package/local-maven-repo/host/exp/exponent/expo.modules.font/58.0.0/expo.modules.font-58.0.0.module.sha512 +0 -1
  245. package/local-maven-repo/host/exp/exponent/expo.modules.font/58.0.0/expo.modules.font-58.0.0.pom +0 -64
  246. package/local-maven-repo/host/exp/exponent/expo.modules.font/58.0.0/expo.modules.font-58.0.0.pom.md5 +0 -1
  247. package/local-maven-repo/host/exp/exponent/expo.modules.font/58.0.0/expo.modules.font-58.0.0.pom.sha1 +0 -1
  248. package/local-maven-repo/host/exp/exponent/expo.modules.font/58.0.0/expo.modules.font-58.0.0.pom.sha256 +0 -1
  249. package/local-maven-repo/host/exp/exponent/expo.modules.font/58.0.0/expo.modules.font-58.0.0.pom.sha512 +0 -1
  250. package/plugin/.tsbuildinfo +0 -1
  251. package/plugin/tsconfig.json +0 -10
  252. package/plugin/tsconfig.tsbuildinfo +0 -1
  253. package/tsconfig.all.json +0 -11
  254. package/tsconfig.json +0 -10
package/CHANGELOG.md CHANGED
@@ -10,6 +10,28 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 58.0.2 — 2026-09-21
14
+
15
+ ### 🐛 Bug fixes
16
+
17
+ - [iOS] Import `UIKit` in the `UIFont` extension, which relied on another file in the module importing it. ([#50277](https://github.com/expo/expo/pull/50277) by [@chrfalch](https://github.com/chrfalch))
18
+
19
+ ## 58.0.1 — 2026-09-15
20
+
21
+ ### 🛠 Breaking changes
22
+
23
+ - [web] Removed `FontResource.testString`. The CSS Font Loading API detects font loading without a test string. ([#49497](https://github.com/expo/expo/pull/49497) by [@vonovak](https://github.com/vonovak))
24
+
25
+ ### 🎉 New features
26
+
27
+ - [android][ios] Select a face of a multi-face font family with the `fontWeight` and `fontStyle` style props. ([#49486](https://github.com/expo/expo/pull/49486) by [@vonovak](https://github.com/vonovak))
28
+ - Support loading multiple font weights and styles per `fontFamily` in `useFonts`/`loadAsync` on Android, iOS, and web. Use `fontWeight` and `fontStyle` style props to select the correct face. ([#47939](https://github.com/expo/expo/pull/47939) by [@bryanmylee](https://github.com/bryanmylee))
29
+ - [web] Accept a variable-font weight range, for example `weight: '100 900'`, on a font face. Android and iOS ignore a range and read the weight from the font file. ([#49487](https://github.com/expo/expo/pull/49487) by [@vonovak](https://github.com/vonovak))
30
+
31
+ ### 💡 Others
32
+
33
+ - [web] Replaced the `fontfaceobserver` dependency with the CSS Font Loading API, so `loadAsync` also waits for the font on Safari and iOS browsers. ([#49497](https://github.com/expo/expo/pull/49497) by [@vonovak](https://github.com/vonovak))
34
+
13
35
  ## 58.0.0 — 2026-09-10
14
36
 
15
37
  ### 🛠 Breaking changes
@@ -4,13 +4,13 @@ plugins {
4
4
  }
5
5
 
6
6
  group = 'host.exp.exponent'
7
- version = '58.0.0'
7
+ version = '58.0.2'
8
8
 
9
9
  android {
10
10
  namespace "expo.modules.font"
11
11
  defaultConfig {
12
12
  versionCode 29
13
- versionName "58.0.0"
13
+ versionName "58.0.2"
14
14
  testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
15
15
  }
16
16
  }
@@ -0,0 +1,66 @@
1
+ package expo.modules.font
2
+
3
+ import expo.modules.kotlin.exception.CodedException
4
+
5
+ private const val MIN_WEIGHT = 1
6
+ private const val MAX_WEIGHT = 1000
7
+ private const val DEFAULT_WEIGHT = 400
8
+
9
+ // Kept free of android.* imports so it can be unit-tested on the plain JVM.
10
+ object FontFamilyFaces {
11
+ private fun isItalic(style: String?) = style == "italic"
12
+
13
+ fun assertWeightsInRange(fontFamilyName: String, faces: List<FontFaceRecord>) {
14
+ for (face in faces) {
15
+ val weight = face.weight ?: DEFAULT_WEIGHT
16
+ if (weight !in MIN_WEIGHT..MAX_WEIGHT) {
17
+ throw CodedException(
18
+ "Font face '${face.localUri}' for family '$fontFamilyName' declares weight $weight, " +
19
+ "but weight must be between $MIN_WEIGHT and $MAX_WEIGHT. " +
20
+ "Set the 'weight' of this face to a value in that range."
21
+ )
22
+ }
23
+ }
24
+ }
25
+
26
+ // Call only with resolved values (after `setWeight`/`setSlant`).
27
+ fun assertNoDuplicateFaces(fontFamilyName: String, faces: List<FontFaceRecord>) {
28
+ val seenFaces = mutableMapOf<Pair<Int, Boolean>, String>()
29
+
30
+ for (face in faces) {
31
+ val weight = face.weight ?: DEFAULT_WEIGHT
32
+ val italic = isItalic(face.style)
33
+ val key = weight to italic
34
+ val conflictingUri = seenFaces[key]
35
+ if (conflictingUri != null) {
36
+ throw CodedException(
37
+ "Font family '$fontFamilyName' declares two faces with weight $weight and " +
38
+ "style '${if (italic) "italic" else "normal"}': '$conflictingUri' and '${face.localUri}'. " +
39
+ "Give each face of a family a distinct weight or style so the correct file is selected at render time."
40
+ )
41
+ }
42
+ seenFaces[key] = face.localUri
43
+ }
44
+ }
45
+
46
+ // The face to load when only one can be (below API 29).
47
+ fun defaultFaceIndex(faces: List<FontFaceRecord>): Int {
48
+ var bestIndex = 0
49
+ var bestDistance = Int.MAX_VALUE
50
+ var bestIsItalic = true
51
+
52
+ faces.forEachIndexed { index, face ->
53
+ val distance = kotlin.math.abs((face.weight ?: DEFAULT_WEIGHT) - DEFAULT_WEIGHT)
54
+ val isItalic = isItalic(face.style)
55
+
56
+ val isBetter = distance < bestDistance || (distance == bestDistance && bestIsItalic && !isItalic)
57
+ if (isBetter) {
58
+ bestIndex = index
59
+ bestDistance = distance
60
+ bestIsItalic = isItalic
61
+ }
62
+ }
63
+
64
+ return bestIndex
65
+ }
66
+ }
@@ -8,6 +8,7 @@ import android.os.Build
8
8
  import android.util.Log
9
9
  import androidx.annotation.RequiresApi
10
10
  import com.facebook.react.common.assets.ReactFontManager
11
+ import expo.modules.kotlin.exception.CodedException
11
12
  import expo.modules.kotlin.exception.Exceptions
12
13
  import expo.modules.kotlin.modules.Module
13
14
  import expo.modules.kotlin.modules.ModuleDefinition
@@ -32,24 +33,57 @@ open class FontLoaderModule : Module() {
32
33
  return@Function getLoadedFonts()
33
34
  }
34
35
 
35
- AsyncFunction("loadAsync") { fontFamilyName: String, localUri: String ->
36
- val context = appContext.reactContext ?: throw Exceptions.ReactContextLost()
36
+ fun registerTypeface(fontFamilyName: String, typeface: Typeface) {
37
+ ReactFontManager.getInstance().addCustomFont(fontFamilyName, typeface)
38
+ loadedFonts = getLoadedFonts().toMutableSet().apply { add(fontFamilyName) }.toList()
39
+ }
37
40
 
38
- // TODO(nikki): make sure path is in experience's scope
39
- val source = FontSource.resolve(localUri, fontFamilyName, context)
41
+ AsyncFunction("loadAsync") { fontFamilyName: String, localUri: String ->
42
+ registerTypeface(fontFamilyName, loadSingleFaceTypeface(fontFamilyName, localUri))
43
+ }
40
44
 
41
- val instanced = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
42
- buildVariableWeightTypeface(fontFamilyName, source)
43
- } else {
44
- // This needs `android.graphics.fonts`, which API 29 added. Before API 29, every weight
45
- // comes from the one weight that Android loads, and Android synthesizes bold from it.
46
- null
45
+ AsyncFunction("loadFontFamilyAsync") { fontFamilyName: String, faces: List<FontFaceRecord> ->
46
+ if (faces.isEmpty()) {
47
+ throw CodedException(
48
+ "Could not load font family '$fontFamilyName' because 'faces' is empty. Pass at " +
49
+ "least one face with a 'localUri' pointing to a font file."
50
+ )
47
51
  }
48
- val typeface = instanced ?: source.load()
52
+ FontFamilyFaces.assertWeightsInRange(fontFamilyName, faces)
49
53
 
50
- ReactFontManager.getInstance().addCustomFont(fontFamilyName, typeface)
51
- loadedFonts = getLoadedFonts().toMutableSet().apply { add(fontFamilyName) }.toList()
54
+ registerTypeface(fontFamilyName, familyTypeface(fontFamilyName, faces))
55
+ }
56
+ }
57
+
58
+ // Below API 29, `android.graphics.fonts.FontFamily` doesn't exist, so only the default face
59
+ // loads and Android synthesizes bold/italic from it.
60
+ private fun familyTypeface(fontFamilyName: String, faces: List<FontFaceRecord>): Typeface {
61
+ val face = faces.first()
62
+ if (faces.size == 1 && face.weight == null && face.style == null) {
63
+ return loadSingleFaceTypeface(fontFamilyName, face.localUri)
64
+ }
65
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
66
+ val defaultFace = faces[FontFamilyFaces.defaultFaceIndex(faces)]
67
+ return loadSingleFaceTypeface(fontFamilyName, defaultFace.localUri)
52
68
  }
69
+ return buildMultiFaceTypeface(fontFamilyName, faces)
70
+ }
71
+
72
+ private fun loadSingleFaceTypeface(fontFamilyName: String, localUri: String): Typeface {
73
+ val source = FontSource.resolve(localUri, fontFamilyName, context)
74
+
75
+ val instanced = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
76
+ buildVariableWeightTypeface(fontFamilyName, source)
77
+ } else {
78
+ null
79
+ }
80
+ return instanced ?: source.load()
81
+ }
82
+
83
+ @RequiresApi(Build.VERSION_CODES.Q)
84
+ private fun buildMultiFaceTypeface(fontFamilyName: String, faces: List<FontFaceRecord>): Typeface {
85
+ val sources = faces.map { FontSource.resolve(it.localUri, fontFamilyName, context) }
86
+ return MultiFaceTypeface.build(fontFamilyName, faces, sources)
53
87
  }
54
88
 
55
89
  /**
@@ -0,0 +1,17 @@
1
+ package expo.modules.font
2
+
3
+ import expo.modules.kotlin.records.Field
4
+ import expo.modules.kotlin.records.Record
5
+ import expo.modules.kotlin.types.OptimizedRecord
6
+
7
+ @OptimizedRecord
8
+ data class FontFaceRecord(
9
+ @Field
10
+ val localUri: String = "",
11
+
12
+ @Field
13
+ val weight: Int? = null,
14
+
15
+ @Field
16
+ val style: String? = null
17
+ ) : Record
@@ -4,7 +4,10 @@ package expo.modules.font
4
4
 
5
5
  import android.content.Context
6
6
  import android.graphics.Typeface
7
+ import android.graphics.fonts.Font
7
8
  import android.net.Uri
9
+ import android.os.Build
10
+ import androidx.annotation.RequiresApi
8
11
  import expo.modules.kotlin.exception.CodedException
9
12
  import java.io.File
10
13
  import java.io.FileInputStream
@@ -35,6 +38,10 @@ internal sealed interface FontSource {
35
38
  */
36
39
  fun readForInstancing(): ByteBuffer?
37
40
 
41
+ // Reads the file itself; no [readForInstancing] buffer needed.
42
+ @RequiresApi(Build.VERSION_CODES.Q)
43
+ fun newFontBuilder(): Font.Builder
44
+
38
45
  class Asset(private val path: String, private val context: Context) : FontSource {
39
46
  override fun load(): Typeface =
40
47
  Typeface.createFromAsset(context.assets, path)
@@ -49,6 +56,9 @@ internal sealed interface FontSource {
49
56
 
50
57
  readWholeFont(header, input)
51
58
  }
59
+
60
+ @RequiresApi(Build.VERSION_CODES.Q)
61
+ override fun newFontBuilder(): Font.Builder = Font.Builder(context.assets, path)
52
62
  }
53
63
 
54
64
  class LocalFile(private val file: File) : FontSource {
@@ -60,6 +70,9 @@ internal sealed interface FontSource {
60
70
  }
61
71
  return mapped.takeIf { FontVariationAxes.declaresVariations(it) }
62
72
  }
73
+
74
+ @RequiresApi(Build.VERSION_CODES.Q)
75
+ override fun newFontBuilder(): Font.Builder = Font.Builder(file)
63
76
  }
64
77
 
65
78
  companion object {
@@ -0,0 +1,130 @@
1
+ // Copyright 2015-present 650 Industries. All rights reserved.
2
+
3
+ package expo.modules.font
4
+
5
+ import android.graphics.Typeface
6
+ import android.graphics.fonts.Font
7
+ import android.graphics.fonts.FontFamily
8
+ import android.graphics.fonts.FontStyle
9
+ import android.os.Build
10
+ import androidx.annotation.RequiresApi
11
+ import expo.modules.kotlin.exception.CodedException
12
+ import java.io.IOException
13
+ import java.nio.ByteBuffer
14
+ import kotlin.math.abs
15
+
16
+ /**
17
+ * Builds one [Typeface] from the faces of a font family. `fontWeight` and `fontStyle` then select
18
+ * between them.
19
+ *
20
+ * A face that is a variable font keeps its `wght` axis: a declared weight pins the face to that
21
+ * weight's instance, and a face with no declared weight contributes one instance per weight that
22
+ * `fontWeight` can request. Without the instances, every weight would render the file's default.
23
+ */
24
+ @RequiresApi(Build.VERSION_CODES.Q)
25
+ internal object MultiFaceTypeface {
26
+ /** [faces] and [sources] pair up by index. */
27
+ fun build(
28
+ fontFamilyName: String,
29
+ faces: List<FontFaceRecord>,
30
+ sources: List<FontSource>
31
+ ): Typeface {
32
+ val builtFaces = faces.zip(sources).map { (face, source) ->
33
+ try {
34
+ buildFace(face, source)
35
+ } catch (e: IOException) {
36
+ throw CodedException(
37
+ "Could not read font face '${face.localUri}' for family '$fontFamilyName'. The file " +
38
+ "may be corrupted or in a format Android can't parse. Open it in a font editor to " +
39
+ "check it, or replace it with a valid .ttf or .otf file.",
40
+ e
41
+ )
42
+ }
43
+ }
44
+
45
+ // The duplicate check runs on each face's primary font. The extra instances of a variable
46
+ // face are not declared by the caller, so a collision there is resolved below, not an error.
47
+ val resolvedFaces = faces.zip(builtFaces).map { (face, built) ->
48
+ FontFaceRecord(
49
+ localUri = face.localUri,
50
+ weight = built.primary.style.weight,
51
+ style = if (built.primary.style.slant == FontStyle.FONT_SLANT_ITALIC) "italic" else "normal"
52
+ )
53
+ }
54
+ FontFamilyFaces.assertNoDuplicateFaces(fontFamilyName, resolvedFaces)
55
+
56
+ // Primaries first: a weight and slant a face declares wins over another face's instance of it.
57
+ val seenStyles = mutableSetOf<Pair<Int, Int>>()
58
+ val fonts = mutableListOf<Font>()
59
+ for (built in builtFaces) {
60
+ seenStyles.add(built.primary.styleKey)
61
+ fonts.add(built.primary)
62
+ }
63
+ for (built in builtFaces) {
64
+ for (instance in built.instances) {
65
+ if (seenStyles.add(instance.styleKey)) {
66
+ fonts.add(instance)
67
+ }
68
+ }
69
+ }
70
+
71
+ val familyBuilder = FontFamily.Builder(fonts[0])
72
+ for (font in fonts.drop(1)) {
73
+ try {
74
+ familyBuilder.addFont(font)
75
+ } catch (e: IllegalArgumentException) {
76
+ throw CodedException(
77
+ "expo-font couldn't build the font family '$fontFamilyName' because of an internal " +
78
+ "error. Please report this at https://github.com/expo/expo/issues.",
79
+ e
80
+ )
81
+ }
82
+ }
83
+
84
+ return VariableTypefaces.wrapWithSystemFallback(familyBuilder.build())
85
+ }
86
+
87
+ /**
88
+ * The fonts one face contributes to the family. [primary] carries the face's declared (or the
89
+ * file's own) weight and slant; [instances] cover the rest of a variable face's `wght` axis.
90
+ */
91
+ private class BuiltFace(val primary: Font, val instances: List<Font> = emptyList())
92
+
93
+ private val Font.styleKey: Pair<Int, Int>
94
+ get() = style.weight to style.slant
95
+
96
+ private fun buildFace(face: FontFaceRecord, source: FontSource): BuiltFace {
97
+ val fontData = source.readForInstancing()
98
+ val axis = fontData?.let { FontVariationAxes.readWeightAxis(it) }
99
+ if (axis == null) {
100
+ val builder = source.newFontBuilder()
101
+ face.weight?.let { builder.setWeight(it) }
102
+ face.style?.let { builder.setSlant(slantFor(it)) }
103
+ return BuiltFace(builder.build())
104
+ }
105
+
106
+ if (face.weight != null) {
107
+ // A declared weight pins the face to that instance; the clamp keeps the axis ends.
108
+ return BuiltFace(instance(fontData, face.weight.coerceIn(axis), face.weight, face.style))
109
+ }
110
+
111
+ // `FontFamily.Builder.buildVariableFamily` can't replace the fixed instances here: it exists
112
+ // only on Android 15 and later, and accepts nothing but one upright font plus at most one
113
+ // italic one. The instance nearest the regular weight stands in as the face's primary, which
114
+ // is also the weight the face resolves to today when the whole file is loaded as one font.
115
+ val instances = FontVariationAxes.weightsFor(axis).map { instance(fontData, it, it, face.style) }
116
+ val primary = instances.minByOrNull { abs(it.style.weight - FontStyle.FONT_WEIGHT_NORMAL) }!!
117
+ return BuiltFace(primary, instances)
118
+ }
119
+
120
+ private fun instance(fontData: ByteBuffer, axisWeight: Int, weight: Int, style: String?): Font {
121
+ val builder = Font.Builder(fontData)
122
+ .setFontVariationSettings("'wght' $axisWeight")
123
+ .setWeight(weight)
124
+ style?.let { builder.setSlant(slantFor(it)) }
125
+ return builder.build()
126
+ }
127
+
128
+ private fun slantFor(style: String) =
129
+ if (style == "italic") FontStyle.FONT_SLANT_ITALIC else FontStyle.FONT_SLANT_UPRIGHT
130
+ }
@@ -36,10 +36,13 @@ internal object VariableTypefaces {
36
36
  ?: buildInstancedFamily(fontData)
37
37
  ?: return null
38
38
 
39
- return Typeface.CustomFallbackBuilder(family)
39
+ return wrapWithSystemFallback(family)
40
+ }
41
+
42
+ fun wrapWithSystemFallback(family: FontFamily): Typeface =
43
+ Typeface.CustomFallbackBuilder(family)
40
44
  .setSystemFallback(SYSTEM_FALLBACK)
41
45
  .build()
42
- }
43
46
 
44
47
  /**
45
48
  * The family that Android builds. It covers the axis continuously, not at nine fixed steps. It
@@ -1,7 +1,13 @@
1
1
  import type { ServerFontResourceDescriptor, UnloadFontOptions } from './Font.types';
2
+ export type NativeFontFace = {
3
+ localUri: string;
4
+ weight?: number;
5
+ style?: 'normal' | 'italic';
6
+ };
2
7
  export type ExpoFontLoaderModule = {
3
8
  getLoadedFonts: () => string[];
4
9
  loadAsync: (fontFamilyName: string, localUriOrWebAsset: any) => Promise<void>;
10
+ loadFontFamilyAsync: (fontFamilyName: string, faces: NativeFontFace[]) => Promise<void>;
5
11
  unloadAllAsync?: () => Promise<void>;
6
12
  unloadAsync?: (fontFamilyName: string, options?: UnloadFontOptions) => Promise<void>;
7
13
  isLoaded?: (fontFamilyName: string, options?: UnloadFontOptions) => boolean;
@@ -5,6 +5,9 @@ const m = typeof window === 'undefined' ? {
5
5
  },
6
6
  loadAsync () {
7
7
  return Promise.resolve();
8
+ },
9
+ loadFontFamilyAsync () {
10
+ return Promise.resolve();
8
11
  }
9
12
  } : requireNativeModule('ExpoFontLoader');
10
13
  export default m;
@@ -1 +1 @@
1
- {"version":3,"sources":["ExpoFontLoader.ts"],"sourcesContent":[null],"names":["requireNativeModule","m","window","getLoadedFonts","loadAsync","Promise","resolve"],"mappings":"AAAA,SAASA,mBAAmB,QAAQ,oBAAoB;AAexD,MAAMC,IACJ,OAAOC,WAAW,cAEd;IACEC;QACE,OAAO,EAAE;IACX;IACAC;QACE,OAAOC,QAAQC,OAAO;IACxB;AACF,IACAN,oBAAoB;AAC1B,eAAeC,EAAE"}
1
+ {"version":3,"sources":["ExpoFontLoader.ts"],"sourcesContent":[null],"names":["requireNativeModule","m","window","getLoadedFonts","loadAsync","Promise","resolve","loadFontFamilyAsync"],"mappings":"AAAA,SAASA,mBAAmB,QAAQ,oBAAoB;AAuBxD,MAAMC,IACJ,OAAOC,WAAW,cAEd;IACEC;QACE,OAAO,EAAE;IACX;IACAC;QACE,OAAOC,QAAQC,OAAO;IACxB;IACAC;QACE,OAAOF,QAAQC,OAAO;IACxB;AACF,IACAN,oBAAoB;AAC1B,eAAeC,EAAE"}
@@ -1,6 +1,9 @@
1
1
  import type { ExpoFontLoaderModule } from './ExpoFontLoader';
2
- import { type FontResource } from './Font.types';
3
- declare const ExpoFontLoader: Required<ExpoFontLoaderModule>;
2
+ import type { UnloadFontOptions } from './Font';
3
+ import type { FontResource } from './Font.types';
4
+ export declare function _matchesFontFaceOptions(rule: Pick<CSSFontFaceRule, 'style'>, fontFamilyName: string, options?: UnloadFontOptions): boolean;
5
+ export declare function _fontFaceRuleSrcMatches(rule: Pick<CSSFontFaceRule, 'style'>, uri: string | number | undefined): boolean;
6
+ declare const ExpoFontLoader: Required<Omit<ExpoFontLoaderModule, 'loadFontFamilyAsync'>>;
4
7
  declare const _default: typeof ExpoFontLoader;
5
8
  export default _default;
6
9
  export declare function _createWebFontTemplate(fontFamily: string, resource: FontResource): string;
@@ -1,6 +1,4 @@
1
1
  import { CodedError, registerWebModule } from 'expo-modules-core';
2
- import FontObserver from 'fontfaceobserver';
3
- import { FontDisplay } from './Font.types';
4
2
  import { addServerFont, getLoadedServerFonts, getServerResourceDescriptors as readServerResourceDescriptors, isServerFontLoaded } from './serverContext';
5
3
  function getFontFaceStyleSheet() {
6
4
  if (typeof window === 'undefined') {
@@ -44,11 +42,56 @@ function normalizeFontFamilyName(fontFamily) {
44
42
  }
45
43
  return trimmed;
46
44
  }
45
+ function canonicalCssWeight(weight) {
46
+ if (weight == null || weight === '') {
47
+ return undefined;
48
+ }
49
+ if (typeof weight === 'number') {
50
+ return Number.isFinite(weight) ? weight : undefined;
51
+ }
52
+ const lower = weight.trim().toLowerCase();
53
+ if (lower === 'normal') {
54
+ return 400;
55
+ }
56
+ if (lower === 'bold') {
57
+ return 700;
58
+ }
59
+ const numeric = Number(lower);
60
+ return Number.isFinite(numeric) ? numeric : lower;
61
+ }
62
+ // jsdom doesn't implement `CSSFontFaceRule`, so tests call this directly with plain `{ style }` objects.
63
+ export function _matchesFontFaceOptions(rule, fontFamilyName, options) {
64
+ if (normalizeFontFamilyName(rule.style.fontFamily) !== fontFamilyName) {
65
+ return false;
66
+ }
67
+ if (options?.display && options.display !== rule.style.fontDisplay) {
68
+ return false;
69
+ }
70
+ if (options?.weight != null && canonicalCssWeight(options.weight) !== canonicalCssWeight(rule.style.fontWeight)) {
71
+ return false;
72
+ }
73
+ if (options?.style != null && options.style !== rule.style.fontStyle) {
74
+ return false;
75
+ }
76
+ return true;
77
+ }
47
78
  function getFontFaceRulesMatchingResource(fontFamilyName, options) {
48
- const rules = getFontFaceRules();
49
- return rules.filter(({ rule })=>{
50
- return normalizeFontFamilyName(rule.style.fontFamily) === fontFamilyName && (options && options.display ? options.display === rule.style.fontDisplay : true);
51
- });
79
+ return getFontFaceRules().filter(({ rule })=>_matchesFontFaceOptions(rule, fontFamilyName, options));
80
+ }
81
+ export function _fontFaceRuleSrcMatches(rule, uri) {
82
+ const src = rule.style.getPropertyValue('src');
83
+ const match = src.match(/url\((['"]?)([^'")]*)\1\)/);
84
+ if (!match) {
85
+ // Every rule carries a `url(...)`; an unreadable `src` means the engine doesn't expose it.
86
+ return true;
87
+ }
88
+ let ruleUri = match[2] ?? '';
89
+ try {
90
+ ruleUri = decodeURIComponent(ruleUri);
91
+ } catch {
92
+ // decodeURIComponent throws on malformed percent-encoding; compare the raw value instead.
93
+ }
94
+ return ruleUri === String(uri);
52
95
  }
53
96
  const ExpoFontLoader = {
54
97
  async unloadAllAsync () {
@@ -62,7 +105,11 @@ const ExpoFontLoader = {
62
105
  const sheet = getFontFaceStyleSheet();
63
106
  if (!sheet) return;
64
107
  const items = getFontFaceRulesMatchingResource(fontFamilyName, options);
65
- for (const item of items.reverse()){
108
+ // Descending: `deleteRule` shifts every later index down by one.
109
+ const descending = [
110
+ ...items
111
+ ].sort((a, b)=>b.index - a.index);
112
+ for (const item of descending){
66
113
  sheet.deleteRule(item.index);
67
114
  }
68
115
  },
@@ -86,8 +133,16 @@ const ExpoFontLoader = {
86
133
  if (typeof window === 'undefined') {
87
134
  return getLoadedServerFonts();
88
135
  }
89
- const rules = getFontFaceRules();
90
- return rules.map(({ rule })=>normalizeFontFamilyName(rule.style.fontFamily));
136
+ const seen = new Set();
137
+ const families = [];
138
+ for (const { rule } of getFontFaceRules()){
139
+ const name = normalizeFontFamilyName(rule.style.fontFamily);
140
+ if (!seen.has(name)) {
141
+ seen.add(name);
142
+ families.push(name);
143
+ }
144
+ }
145
+ return families;
91
146
  },
92
147
  isLoaded (fontFamilyName, resource = {}) {
93
148
  if (typeof window === 'undefined') {
@@ -117,19 +172,39 @@ const ExpoFontLoader = {
117
172
  }
118
173
  const style = getStyleElement();
119
174
  document.head.appendChild(style);
120
- const res = getFontFaceRulesMatchingResource(fontFamilyName, resource);
121
- if (!res.length) {
175
+ const alreadyLoaded = getFontFaceRulesMatchingResource(fontFamilyName, resource).some(({ rule })=>_fontFaceRuleSrcMatches(rule, resource.uri));
176
+ if (!alreadyLoaded) {
122
177
  _createWebStyle(fontFamilyName, resource);
123
178
  }
124
- if (!isFontLoadingListenerSupported()) {
179
+ if (typeof document.fonts?.load !== 'function') {
125
180
  return Promise.resolve();
126
181
  }
127
- return new FontObserver(fontFamilyName, {
128
- // @ts-expect-error: TODO(@kitten): Typings indicate that the polyfill may not support this?
129
- display: resource.display
130
- }).load(resource.testString ?? null, 12000);
182
+ // Resolve when the browser has fetched the file, so text renders with it on resolution. The
183
+ // face is selected with the `font` shorthand syntax; skip a descriptor the CSS sanitization
184
+ // in `_createWebFontTemplate` drops, and a range weight, which the shorthand can't express.
185
+ let shorthand = '';
186
+ if (typeof resource.style === 'string' && CSS_IDENT_RE.test(resource.style)) {
187
+ shorthand += `${resource.style} `;
188
+ }
189
+ if (typeof resource.weight === 'number' && Number.isFinite(resource.weight) || typeof resource.weight === 'string' && (CSS_IDENT_RE.test(resource.weight) || CSS_WEIGHT_NUMERIC_RE.test(resource.weight) && !resource.weight.includes(' '))) {
190
+ shorthand += `${resource.weight} `;
191
+ }
192
+ return withLoadTimeout(document.fonts.load(`${shorthand}1em ${JSON.stringify(fontFamilyName)}`), fontFamilyName);
131
193
  }
132
194
  };
195
+ const FONT_LOAD_TIMEOUT_MS = 12000;
196
+ function withLoadTimeout(loading, fontFamilyName) {
197
+ let timer;
198
+ const timingOut = new Promise((_resolve, reject)=>{
199
+ timer = setTimeout(()=>{
200
+ reject(new CodedError('ERR_DOWNLOAD', `Fetching the font family "${fontFamilyName}" timed out after ${FONT_LOAD_TIMEOUT_MS}ms. ` + `Check that the font URL is reachable and served with CORS headers.`));
201
+ }, FONT_LOAD_TIMEOUT_MS);
202
+ });
203
+ return Promise.race([
204
+ loading,
205
+ timingOut
206
+ ]).finally(()=>clearTimeout(timer)).then(()=>undefined);
207
+ }
133
208
  const isServer = process.env.EXPO_OS === 'web' && typeof window === 'undefined';
134
209
  function createExpoFontLoader() {
135
210
  return ExpoFontLoader;
@@ -149,9 +224,26 @@ function getStyleElement() {
149
224
  return styleElement;
150
225
  }
151
226
  const CSS_IDENT_RE = /^[a-zA-Z_-][\w-]*$/;
227
+ // CSS font weights run from 1 to 1000, alone or as a variable-font range ('100 900').
228
+ // No leading zero: '0400' is not a weight.
229
+ const CSS_WEIGHT_NUMERIC_RE = /^(1000|[1-9]\d{0,2})( (1000|[1-9]\d{0,2}))?$/;
152
230
  export function _createWebFontTemplate(fontFamily, resource) {
153
- const display = typeof resource.display === 'string' && CSS_IDENT_RE.test(resource.display) ? resource.display : FontDisplay.AUTO;
154
- return `@font-face{font-family:${JSON.stringify(fontFamily)};src:url(${JSON.stringify(resource.uri)});font-display:${display}}`;
231
+ const declarations = [
232
+ `font-family:${JSON.stringify(fontFamily)}`,
233
+ `src:url(${JSON.stringify(resource.uri)})`
234
+ ];
235
+ if (typeof resource.display === 'string' && CSS_IDENT_RE.test(resource.display)) {
236
+ declarations.push(`font-display:${resource.display}`);
237
+ }
238
+ if (typeof resource.weight === 'number' && Number.isFinite(resource.weight)) {
239
+ declarations.push(`font-weight:${resource.weight}`);
240
+ } else if (typeof resource.weight === 'string' && (CSS_IDENT_RE.test(resource.weight) || CSS_WEIGHT_NUMERIC_RE.test(resource.weight))) {
241
+ declarations.push(`font-weight:${resource.weight}`);
242
+ }
243
+ if (typeof resource.style === 'string' && CSS_IDENT_RE.test(resource.style)) {
244
+ declarations.push(`font-style:${resource.style}`);
245
+ }
246
+ return `@font-face{${declarations.join(';')}}`;
155
247
  }
156
248
  function _createWebStyle(fontFamily, resource) {
157
249
  const fontStyle = _createWebFontTemplate(fontFamily, resource);
@@ -167,16 +259,5 @@ function _createWebStyle(fontFamily, resource) {
167
259
  }
168
260
  return styleElement;
169
261
  }
170
- function isFontLoadingListenerSupported() {
171
- const { userAgent } = window.navigator;
172
- // WebKit is broken https://github.com/bramstein/fontfaceobserver/issues/95
173
- const isIOS = !!userAgent.match(/iPad|iPhone/i);
174
- const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
175
- // Edge is broken https://github.com/bramstein/fontfaceobserver/issues/109#issuecomment-333356795
176
- const isEdge = userAgent.includes('Edge');
177
- // Internet Explorer
178
- const isIE = userAgent.includes('Trident');
179
- return !isSafari && !isIOS && !isEdge && !isIE;
180
- }
181
262
 
182
263
  //# sourceMappingURL=ExpoFontLoader.web.js.map