Merge "Upgrade harfbuzz_ng to 8.0.1" into main
diff --git a/.ci/requirements-fonttools.in b/.ci/requirements-fonttools.in
new file mode 100644
index 0000000..d10c4b7
--- /dev/null
+++ b/.ci/requirements-fonttools.in
@@ -0,0 +1 @@
+fonttools
\ No newline at end of file
diff --git a/.ci/requirements-fonttools.txt b/.ci/requirements-fonttools.txt
new file mode 100644
index 0000000..9d28068
--- /dev/null
+++ b/.ci/requirements-fonttools.txt
@@ -0,0 +1,42 @@
+#
+# This file is autogenerated by pip-compile with Python 3.10
+# by the following command:
+#
+# pip-compile --generate-hashes .ci/requirements-fonttools.in
+#
+fonttools==4.40.0 \
+ --hash=sha256:00ab569b2a3e591e00425023ade87e8fef90380c1dde61be7691cb524ca5f743 \
+ --hash=sha256:022c4a16b412293e7f1ce21b8bab7a6f9d12c4ffdf171fdc67122baddb973069 \
+ --hash=sha256:05171f3c546f64d78569f10adc0de72561882352cac39ec7439af12304d8d8c0 \
+ --hash=sha256:14037c31138fbd21847ad5e5441dfdde003e0a8f3feb5812a1a21fd1c255ffbd \
+ --hash=sha256:15abb3d055c1b2dff9ce376b6c3db10777cb74b37b52b78f61657634fd348a0d \
+ --hash=sha256:18ea64ac43e94c9e0c23d7a9475f1026be0e25b10dda8f236fc956188761df97 \
+ --hash=sha256:1a003608400dd1cca3e089e8c94973c6b51a4fb1ef00ff6d7641617b9242e637 \
+ --hash=sha256:1bc4c5b147be8dbc5df9cc8ac5e93ee914ad030fe2a201cc8f02f499db71011d \
+ --hash=sha256:200729d12461e2038700d31f0d49ad5a7b55855dec7525074979a06b46f88505 \
+ --hash=sha256:337b6e83d7ee73c40ea62407f2ce03b07c3459e213b6f332b94a69923b9e1cb9 \
+ --hash=sha256:37467cee0f32cada2ec08bc16c9c31f9b53ea54b2f5604bf25a1246b5f50593a \
+ --hash=sha256:425b74a608427499b0e45e433c34ddc350820b6f25b7c8761963a08145157a66 \
+ --hash=sha256:530c5d35109f3e0cea2535742d6a3bc99c0786cf0cbd7bb2dc9212387f0d908c \
+ --hash=sha256:56d4d85f5374b45b08d2f928517d1e313ea71b4847240398decd0ab3ebbca885 \
+ --hash=sha256:5e00334c66f4e83535384cb5339526d01d02d77f142c23b2f97bd6a4f585497a \
+ --hash=sha256:5fdf60f8a5c6bcce7d024a33f7e4bc7921f5b74e8ea13bccd204f2c8b86f3470 \
+ --hash=sha256:6a8d71b9a5c884c72741868e845c0e563c5d83dcaf10bb0ceeec3b4b2eb14c67 \
+ --hash=sha256:6d5adf4ba114f028fc3f5317a221fd8b0f4ef7a2e5524a2b1e0fd891b093791a \
+ --hash=sha256:7449e5e306f3a930a8944c85d0cbc8429cba13503372a1a40f23124d6fb09b58 \
+ --hash=sha256:7961575221e3da0841c75da53833272c520000d76f7f71274dbf43370f8a1065 \
+ --hash=sha256:7f6e3fa3da923063c286320e728ba2270e49c73386e3a711aa680f4b0747d692 \
+ --hash=sha256:882983279bf39afe4e945109772c2ffad2be2c90983d6559af8b75c19845a80a \
+ --hash=sha256:8a917828dbfdb1cbe50cf40eeae6fbf9c41aef9e535649ed8f4982b2ef65c091 \
+ --hash=sha256:8c4305b171b61040b1ee75d18f9baafe58bd3b798d1670078efe2c92436bfb63 \
+ --hash=sha256:91784e21a1a085fac07c6a407564f4a77feb471b5954c9ee55a4f9165151f6c1 \
+ --hash=sha256:94c915f6716589f78bc00fbc14c5b8de65cfd11ee335d32504f1ef234524cb24 \
+ --hash=sha256:97d95b8301b62bdece1af943b88bcb3680fd385f88346a4a899ee145913b414a \
+ --hash=sha256:a954b90d1473c85a22ecf305761d9fd89da93bbd31dae86e7dea436ad2cb5dc9 \
+ --hash=sha256:aa83b3f151bc63970f39b2b42a06097c5a22fd7ed9f7ba008e618de4503d3895 \
+ --hash=sha256:b802dcbf9bcff74672f292b2466f6589ab8736ce4dcf36f48eb994c2847c4b30 \
+ --hash=sha256:bae8c13abbc2511e9a855d2142c0ab01178dd66b1a665798f357da0d06253e0d \
+ --hash=sha256:c55f1b4109dbc3aeb496677b3e636d55ef46dc078c2a5e3f3db4e90f1c6d2907 \
+ --hash=sha256:eb52c10fda31159c22c7ed85074e05f8b97da8773ea461706c273e31bcbea836 \
+ --hash=sha256:ec468c022d09f1817c691cf884feb1030ef6f1e93e3ea6831b0d8144c06480d1
+ # via -r .ci/requirements-fonttools.in
diff --git a/.ci/requirements.in b/.ci/requirements.in
new file mode 100644
index 0000000..7984b5e
--- /dev/null
+++ b/.ci/requirements.in
@@ -0,0 +1,4 @@
+-r requirements-fonttools.in
+meson==1.1.1
+gcovr==5.0
+ninja
diff --git a/.ci/requirements.txt b/.ci/requirements.txt
new file mode 100644
index 0000000..c4c0f59
--- /dev/null
+++ b/.ci/requirements.txt
@@ -0,0 +1,208 @@
+#
+# This file is autogenerated by pip-compile with Python 3.10
+# by the following command:
+#
+# pip-compile --generate-hashes .ci/requirements.in
+#
+fonttools==4.40.0 \
+ --hash=sha256:00ab569b2a3e591e00425023ade87e8fef90380c1dde61be7691cb524ca5f743 \
+ --hash=sha256:022c4a16b412293e7f1ce21b8bab7a6f9d12c4ffdf171fdc67122baddb973069 \
+ --hash=sha256:05171f3c546f64d78569f10adc0de72561882352cac39ec7439af12304d8d8c0 \
+ --hash=sha256:14037c31138fbd21847ad5e5441dfdde003e0a8f3feb5812a1a21fd1c255ffbd \
+ --hash=sha256:15abb3d055c1b2dff9ce376b6c3db10777cb74b37b52b78f61657634fd348a0d \
+ --hash=sha256:18ea64ac43e94c9e0c23d7a9475f1026be0e25b10dda8f236fc956188761df97 \
+ --hash=sha256:1a003608400dd1cca3e089e8c94973c6b51a4fb1ef00ff6d7641617b9242e637 \
+ --hash=sha256:1bc4c5b147be8dbc5df9cc8ac5e93ee914ad030fe2a201cc8f02f499db71011d \
+ --hash=sha256:200729d12461e2038700d31f0d49ad5a7b55855dec7525074979a06b46f88505 \
+ --hash=sha256:337b6e83d7ee73c40ea62407f2ce03b07c3459e213b6f332b94a69923b9e1cb9 \
+ --hash=sha256:37467cee0f32cada2ec08bc16c9c31f9b53ea54b2f5604bf25a1246b5f50593a \
+ --hash=sha256:425b74a608427499b0e45e433c34ddc350820b6f25b7c8761963a08145157a66 \
+ --hash=sha256:530c5d35109f3e0cea2535742d6a3bc99c0786cf0cbd7bb2dc9212387f0d908c \
+ --hash=sha256:56d4d85f5374b45b08d2f928517d1e313ea71b4847240398decd0ab3ebbca885 \
+ --hash=sha256:5e00334c66f4e83535384cb5339526d01d02d77f142c23b2f97bd6a4f585497a \
+ --hash=sha256:5fdf60f8a5c6bcce7d024a33f7e4bc7921f5b74e8ea13bccd204f2c8b86f3470 \
+ --hash=sha256:6a8d71b9a5c884c72741868e845c0e563c5d83dcaf10bb0ceeec3b4b2eb14c67 \
+ --hash=sha256:6d5adf4ba114f028fc3f5317a221fd8b0f4ef7a2e5524a2b1e0fd891b093791a \
+ --hash=sha256:7449e5e306f3a930a8944c85d0cbc8429cba13503372a1a40f23124d6fb09b58 \
+ --hash=sha256:7961575221e3da0841c75da53833272c520000d76f7f71274dbf43370f8a1065 \
+ --hash=sha256:7f6e3fa3da923063c286320e728ba2270e49c73386e3a711aa680f4b0747d692 \
+ --hash=sha256:882983279bf39afe4e945109772c2ffad2be2c90983d6559af8b75c19845a80a \
+ --hash=sha256:8a917828dbfdb1cbe50cf40eeae6fbf9c41aef9e535649ed8f4982b2ef65c091 \
+ --hash=sha256:8c4305b171b61040b1ee75d18f9baafe58bd3b798d1670078efe2c92436bfb63 \
+ --hash=sha256:91784e21a1a085fac07c6a407564f4a77feb471b5954c9ee55a4f9165151f6c1 \
+ --hash=sha256:94c915f6716589f78bc00fbc14c5b8de65cfd11ee335d32504f1ef234524cb24 \
+ --hash=sha256:97d95b8301b62bdece1af943b88bcb3680fd385f88346a4a899ee145913b414a \
+ --hash=sha256:a954b90d1473c85a22ecf305761d9fd89da93bbd31dae86e7dea436ad2cb5dc9 \
+ --hash=sha256:aa83b3f151bc63970f39b2b42a06097c5a22fd7ed9f7ba008e618de4503d3895 \
+ --hash=sha256:b802dcbf9bcff74672f292b2466f6589ab8736ce4dcf36f48eb994c2847c4b30 \
+ --hash=sha256:bae8c13abbc2511e9a855d2142c0ab01178dd66b1a665798f357da0d06253e0d \
+ --hash=sha256:c55f1b4109dbc3aeb496677b3e636d55ef46dc078c2a5e3f3db4e90f1c6d2907 \
+ --hash=sha256:eb52c10fda31159c22c7ed85074e05f8b97da8773ea461706c273e31bcbea836 \
+ --hash=sha256:ec468c022d09f1817c691cf884feb1030ef6f1e93e3ea6831b0d8144c06480d1
+ # via -r requirements-fonttools.in
+gcovr==5.0 \
+ --hash=sha256:1d80264cbaadff356b3dda71b8c62b3aa803e5b3eb6d526a24932cd6660a2576 \
+ --hash=sha256:8c49ebcfc5a98b56dd900c687aad0258ac86093d2f81a1417905193ab45fe69f
+ # via -r requirements.in
+jinja2==3.1.2 \
+ --hash=sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852 \
+ --hash=sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61
+ # via gcovr
+lxml==4.9.2 \
+ --hash=sha256:01d36c05f4afb8f7c20fd9ed5badca32a2029b93b1750f571ccc0b142531caf7 \
+ --hash=sha256:04876580c050a8c5341d706dd464ff04fd597095cc8c023252566a8826505726 \
+ --hash=sha256:05ca3f6abf5cf78fe053da9b1166e062ade3fa5d4f92b4ed688127ea7d7b1d03 \
+ --hash=sha256:090c6543d3696cbe15b4ac6e175e576bcc3f1ccfbba970061b7300b0c15a2140 \
+ --hash=sha256:0dc313ef231edf866912e9d8f5a042ddab56c752619e92dfd3a2c277e6a7299a \
+ --hash=sha256:0f2b1e0d79180f344ff9f321327b005ca043a50ece8713de61d1cb383fb8ac05 \
+ --hash=sha256:13598ecfbd2e86ea7ae45ec28a2a54fb87ee9b9fdb0f6d343297d8e548392c03 \
+ --hash=sha256:16efd54337136e8cd72fb9485c368d91d77a47ee2d42b057564aae201257d419 \
+ --hash=sha256:1ab8f1f932e8f82355e75dda5413a57612c6ea448069d4fb2e217e9a4bed13d4 \
+ --hash=sha256:223f4232855ade399bd409331e6ca70fb5578efef22cf4069a6090acc0f53c0e \
+ --hash=sha256:2455cfaeb7ac70338b3257f41e21f0724f4b5b0c0e7702da67ee6c3640835b67 \
+ --hash=sha256:2899456259589aa38bfb018c364d6ae7b53c5c22d8e27d0ec7609c2a1ff78b50 \
+ --hash=sha256:2a29ba94d065945944016b6b74e538bdb1751a1db6ffb80c9d3c2e40d6fa9894 \
+ --hash=sha256:2a87fa548561d2f4643c99cd13131acb607ddabb70682dcf1dff5f71f781a4bf \
+ --hash=sha256:2e430cd2824f05f2d4f687701144556646bae8f249fd60aa1e4c768ba7018947 \
+ --hash=sha256:36c3c175d34652a35475a73762b545f4527aec044910a651d2bf50de9c3352b1 \
+ --hash=sha256:3818b8e2c4b5148567e1b09ce739006acfaa44ce3156f8cbbc11062994b8e8dd \
+ --hash=sha256:3ab9fa9d6dc2a7f29d7affdf3edebf6ece6fb28a6d80b14c3b2fb9d39b9322c3 \
+ --hash=sha256:3efea981d956a6f7173b4659849f55081867cf897e719f57383698af6f618a92 \
+ --hash=sha256:4c8f293f14abc8fd3e8e01c5bd86e6ed0b6ef71936ded5bf10fe7a5efefbaca3 \
+ --hash=sha256:5344a43228767f53a9df6e5b253f8cdca7dfc7b7aeae52551958192f56d98457 \
+ --hash=sha256:58bfa3aa19ca4c0f28c5dde0ff56c520fbac6f0daf4fac66ed4c8d2fb7f22e74 \
+ --hash=sha256:5b4545b8a40478183ac06c073e81a5ce4cf01bf1734962577cf2bb569a5b3bbf \
+ --hash=sha256:5f50a1c177e2fa3ee0667a5ab79fdc6b23086bc8b589d90b93b4bd17eb0e64d1 \
+ --hash=sha256:63da2ccc0857c311d764e7d3d90f429c252e83b52d1f8f1d1fe55be26827d1f4 \
+ --hash=sha256:6749649eecd6a9871cae297bffa4ee76f90b4504a2a2ab528d9ebe912b101975 \
+ --hash=sha256:6804daeb7ef69e7b36f76caddb85cccd63d0c56dedb47555d2fc969e2af6a1a5 \
+ --hash=sha256:689bb688a1db722485e4610a503e3e9210dcc20c520b45ac8f7533c837be76fe \
+ --hash=sha256:699a9af7dffaf67deeae27b2112aa06b41c370d5e7633e0ee0aea2e0b6c211f7 \
+ --hash=sha256:6b418afe5df18233fc6b6093deb82a32895b6bb0b1155c2cdb05203f583053f1 \
+ --hash=sha256:76cf573e5a365e790396a5cc2b909812633409306c6531a6877c59061e42c4f2 \
+ --hash=sha256:7b515674acfdcadb0eb5d00d8a709868173acece5cb0be3dd165950cbfdf5409 \
+ --hash=sha256:7b770ed79542ed52c519119473898198761d78beb24b107acf3ad65deae61f1f \
+ --hash=sha256:7d2278d59425777cfcb19735018d897ca8303abe67cc735f9f97177ceff8027f \
+ --hash=sha256:7e91ee82f4199af8c43d8158024cbdff3d931df350252288f0d4ce656df7f3b5 \
+ --hash=sha256:821b7f59b99551c69c85a6039c65b75f5683bdc63270fec660f75da67469ca24 \
+ --hash=sha256:822068f85e12a6e292803e112ab876bc03ed1f03dddb80154c395f891ca6b31e \
+ --hash=sha256:8340225bd5e7a701c0fa98284c849c9b9fc9238abf53a0ebd90900f25d39a4e4 \
+ --hash=sha256:85cabf64adec449132e55616e7ca3e1000ab449d1d0f9d7f83146ed5bdcb6d8a \
+ --hash=sha256:880bbbcbe2fca64e2f4d8e04db47bcdf504936fa2b33933efd945e1b429bea8c \
+ --hash=sha256:8d0b4612b66ff5d62d03bcaa043bb018f74dfea51184e53f067e6fdcba4bd8de \
+ --hash=sha256:8e20cb5a47247e383cf4ff523205060991021233ebd6f924bca927fcf25cf86f \
+ --hash=sha256:925073b2fe14ab9b87e73f9a5fde6ce6392da430f3004d8b72cc86f746f5163b \
+ --hash=sha256:998c7c41910666d2976928c38ea96a70d1aa43be6fe502f21a651e17483a43c5 \
+ --hash=sha256:9b22c5c66f67ae00c0199f6055705bc3eb3fcb08d03d2ec4059a2b1b25ed48d7 \
+ --hash=sha256:9f102706d0ca011de571de32c3247c6476b55bb6bc65a20f682f000b07a4852a \
+ --hash=sha256:a08cff61517ee26cb56f1e949cca38caabe9ea9fbb4b1e10a805dc39844b7d5c \
+ --hash=sha256:a0a336d6d3e8b234a3aae3c674873d8f0e720b76bc1d9416866c41cd9500ffb9 \
+ --hash=sha256:a35f8b7fa99f90dd2f5dc5a9fa12332642f087a7641289ca6c40d6e1a2637d8e \
+ --hash=sha256:a38486985ca49cfa574a507e7a2215c0c780fd1778bb6290c21193b7211702ab \
+ --hash=sha256:a5da296eb617d18e497bcf0a5c528f5d3b18dadb3619fbdadf4ed2356ef8d941 \
+ --hash=sha256:a6e441a86553c310258aca15d1c05903aaf4965b23f3bc2d55f200804e005ee5 \
+ --hash=sha256:a82d05da00a58b8e4c0008edbc8a4b6ec5a4bc1e2ee0fb6ed157cf634ed7fa45 \
+ --hash=sha256:ab323679b8b3030000f2be63e22cdeea5b47ee0abd2d6a1dc0c8103ddaa56cd7 \
+ --hash=sha256:b1f42b6921d0e81b1bcb5e395bc091a70f41c4d4e55ba99c6da2b31626c44892 \
+ --hash=sha256:b23e19989c355ca854276178a0463951a653309fb8e57ce674497f2d9f208746 \
+ --hash=sha256:b264171e3143d842ded311b7dccd46ff9ef34247129ff5bf5066123c55c2431c \
+ --hash=sha256:b26a29f0b7fc6f0897f043ca366142d2b609dc60756ee6e4e90b5f762c6adc53 \
+ --hash=sha256:b64d891da92e232c36976c80ed7ebb383e3f148489796d8d31a5b6a677825efe \
+ --hash=sha256:b9cc34af337a97d470040f99ba4282f6e6bac88407d021688a5d585e44a23184 \
+ --hash=sha256:bc718cd47b765e790eecb74d044cc8d37d58562f6c314ee9484df26276d36a38 \
+ --hash=sha256:be7292c55101e22f2a3d4d8913944cbea71eea90792bf914add27454a13905df \
+ --hash=sha256:c83203addf554215463b59f6399835201999b5e48019dc17f182ed5ad87205c9 \
+ --hash=sha256:c9ec3eaf616d67db0764b3bb983962b4f385a1f08304fd30c7283954e6a7869b \
+ --hash=sha256:ca34efc80a29351897e18888c71c6aca4a359247c87e0b1c7ada14f0ab0c0fb2 \
+ --hash=sha256:ca989b91cf3a3ba28930a9fc1e9aeafc2a395448641df1f387a2d394638943b0 \
+ --hash=sha256:d02a5399126a53492415d4906ab0ad0375a5456cc05c3fc0fc4ca11771745cda \
+ --hash=sha256:d17bc7c2ccf49c478c5bdd447594e82692c74222698cfc9b5daae7ae7e90743b \
+ --hash=sha256:d5bf6545cd27aaa8a13033ce56354ed9e25ab0e4ac3b5392b763d8d04b08e0c5 \
+ --hash=sha256:d6b430a9938a5a5d85fc107d852262ddcd48602c120e3dbb02137c83d212b380 \
+ --hash=sha256:da248f93f0418a9e9d94b0080d7ebc407a9a5e6d0b57bb30db9b5cc28de1ad33 \
+ --hash=sha256:da4dd7c9c50c059aba52b3524f84d7de956f7fef88f0bafcf4ad7dde94a064e8 \
+ --hash=sha256:df0623dcf9668ad0445e0558a21211d4e9a149ea8f5666917c8eeec515f0a6d1 \
+ --hash=sha256:e5168986b90a8d1f2f9dc1b841467c74221bd752537b99761a93d2d981e04889 \
+ --hash=sha256:efa29c2fe6b4fdd32e8ef81c1528506895eca86e1d8c4657fda04c9b3786ddf9 \
+ --hash=sha256:f1496ea22ca2c830cbcbd473de8f114a320da308438ae65abad6bab7867fe38f \
+ --hash=sha256:f49e52d174375a7def9915c9f06ec4e569d235ad428f70751765f48d5926678c
+ # via gcovr
+markupsafe==2.1.3 \
+ --hash=sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e \
+ --hash=sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e \
+ --hash=sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431 \
+ --hash=sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686 \
+ --hash=sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559 \
+ --hash=sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc \
+ --hash=sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c \
+ --hash=sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0 \
+ --hash=sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4 \
+ --hash=sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9 \
+ --hash=sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575 \
+ --hash=sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba \
+ --hash=sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d \
+ --hash=sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3 \
+ --hash=sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00 \
+ --hash=sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155 \
+ --hash=sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac \
+ --hash=sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52 \
+ --hash=sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f \
+ --hash=sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8 \
+ --hash=sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b \
+ --hash=sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24 \
+ --hash=sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea \
+ --hash=sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198 \
+ --hash=sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0 \
+ --hash=sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee \
+ --hash=sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be \
+ --hash=sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2 \
+ --hash=sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707 \
+ --hash=sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6 \
+ --hash=sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58 \
+ --hash=sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779 \
+ --hash=sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636 \
+ --hash=sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c \
+ --hash=sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad \
+ --hash=sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee \
+ --hash=sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc \
+ --hash=sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2 \
+ --hash=sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48 \
+ --hash=sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7 \
+ --hash=sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e \
+ --hash=sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b \
+ --hash=sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa \
+ --hash=sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5 \
+ --hash=sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e \
+ --hash=sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb \
+ --hash=sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9 \
+ --hash=sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57 \
+ --hash=sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc \
+ --hash=sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2
+ # via jinja2
+meson==1.1.1 \
+ --hash=sha256:1c5a46660dfbe5f45ef7588d78429d04ebdb6f84b96b4b015033572d2facbbe5 \
+ --hash=sha256:d04b541f97ca439fb82fab7d0d480988be4bd4e62563a5ca35fadb5400727b1c
+ # via -r requirements.in
+ninja==1.11.1 \
+ --hash=sha256:1c474326e11fba3f8c2582715d79216292e327d3335367c0e87e9647a002cc4a \
+ --hash=sha256:3329b4b7c1694730772522a3ba0ba40fd15c012476ed3e1c9f0fd9e76190394e \
+ --hash=sha256:34753459493543782d87267e4cad63dd4639b07f8394ffe6d4417e9eda05c8a8 \
+ --hash=sha256:3b28b595ed580752240ade7821b6cb7a5a4c6a604c865dc474bd38f06e2eb7f5 \
+ --hash=sha256:4e547bc759c570773d83d110c41fd5ca9a94c0a9a8388f5a3ea37bdf97d002b0 \
+ --hash=sha256:60179bb4f22c88279c53a5402bb5fe81c97c627a28d93c737d1fa067d892115d \
+ --hash=sha256:642cb64d859276998f14972724850e0c5b7febbc1bce3d2065b7e0cb7d3a0b79 \
+ --hash=sha256:6f6465a7efe6473a2a34edab83633594de19d59406a727316e1367ebcc528908 \
+ --hash=sha256:779f228e407c54a8b6e4cbf8f835489998dd250f67bf1b9bd7b8a8ab6bdcdc7b \
+ --hash=sha256:817e2aee2a4d28a708a67bcfba1817ae502c32c6d8ef80e50d63b0f23adf3a08 \
+ --hash=sha256:8cf96f92ccc851c600cb3e1251c34db06f1dd682de79188ad490c33cddc66981 \
+ --hash=sha256:a7a564fe755ddfbdbccb07b0b758e3f8460e5f8ba1adaab40a5eaa2f8c01ce68 \
+ --hash=sha256:ba50a32424912e5f3ee40d791b506a160dc0eeda7de5ad8faebe7aa8006244dc \
+ --hash=sha256:c833a47d39b2d1eee3f9ca886fa1581efd5be6068b82734ac229961ee8748f90 \
+ --hash=sha256:df11b8afea0501883e33faeb1c43d2ef67f466d5f4bd85f9c376e9a93a43a277 \
+ --hash=sha256:edec1053e141253076b2df7ec03a246ff581e9270aa1ca9759397b21e2760e57 \
+ --hash=sha256:f48c3c6eea204062f6bbf089dfc63e1ad41a08640e1da46ef2b30fa426f7ce23
+ # via -r requirements.in
+pygments==2.15.1 \
+ --hash=sha256:8ace4d3c1dd481894b2005f560ead0f9f19ee64fe983366be1a21e171d12775c \
+ --hash=sha256:db2db3deb4b4179f399a09054b023b6a586b76499d36965813c71aa8ed7b5fd1
+ # via gcovr
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 5ace460..8725672 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -4,3 +4,7 @@
directory: "/"
schedule:
interval: "weekly"
+ - package-ecosystem: "pip"
+ directory: "/.ci"
+ schedule:
+ interval: "weekly"
diff --git a/.github/workflows/arm-ci.yml b/.github/workflows/arm-ci.yml
index 3df1316..282977d 100644
--- a/.github/workflows/arm-ci.yml
+++ b/.github/workflows/arm-ci.yml
@@ -15,7 +15,7 @@
container:
image: devkitpro/devkitarm:latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
- name: Configure CMake
run: |
cmake -S . -B build \
diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml
index 7d6e246..5b6a52d 100644
--- a/.github/workflows/cifuzz.yml
+++ b/.github/workflows/cifuzz.yml
@@ -21,7 +21,7 @@
fuzz-seconds: 600
dry-run: false
- name: Upload Crash
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
if: failure() && steps.build.outcome == 'success'
with:
name: artifacts
diff --git a/.github/workflows/configs-build.yml b/.github/workflows/configs-build.yml
index b81ce92..5042aba 100644
--- a/.github/workflows/configs-build.yml
+++ b/.github/workflows/configs-build.yml
@@ -14,7 +14,7 @@
runs-on: ubuntu-20.04
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
- name: install dependencies
run: sudo apt-get install gcc
- name: HB_DISABLE_DEPRECATED
diff --git a/.github/workflows/coverity-scan.yml b/.github/workflows/coverity-scan.yml
index e9fc543..1001967 100644
--- a/.github/workflows/coverity-scan.yml
+++ b/.github/workflows/coverity-scan.yml
@@ -11,7 +11,7 @@
latest:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
- run: sudo apt-get install gcc clang wget git curl pkg-config libfreetype6-dev libglib2.0-dev libicu-dev libgraphite2-dev
diff --git a/.github/workflows/linux-ci.yml b/.github/workflows/linux-ci.yml
index 3ec2eea..950e4ed 100644
--- a/.github/workflows/linux-ci.yml
+++ b/.github/workflows/linux-ci.yml
@@ -16,9 +16,9 @@
steps:
- name: Checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
- name: Setup Ccache
- uses: hendrikmuhs/ccache-action@v1.2
+ uses: hendrikmuhs/ccache-action@ca3acd2731eef11f1572ccb126356c2f9298d35e # v1.2.9
with:
key: ${{ github.job }}-${{ runner.os }}-${{ runner.arch }}
- name: Install Dependencies
@@ -39,7 +39,7 @@
python3 \
python3-setuptools
- name: Install Python Dependencies
- run: sudo pip3 install fonttools meson==0.56.0 gcovr==5.0
+ run: sudo pip3 install -r .ci/requirements.txt --require-hashes
- name: Setup Meson
run: |
ccache --version
@@ -66,6 +66,6 @@
- name: Generate Coverage
run: ninja -Cbuild coverage-xml
- name: Upload Coverage
- uses: codecov/codecov-action@v3
+ uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # v3.1.4
with:
file: build/meson-logs/coverage.xml
diff --git a/.github/workflows/macos-ci.yml b/.github/workflows/macos-ci.yml
index e84cb38..abf2864 100644
--- a/.github/workflows/macos-ci.yml
+++ b/.github/workflows/macos-ci.yml
@@ -15,9 +15,9 @@
steps:
- name: Checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
- name: Setup Ccache
- uses: hendrikmuhs/ccache-action@v1.2
+ uses: hendrikmuhs/ccache-action@ca3acd2731eef11f1572ccb126356c2f9298d35e # v1.2.9
with:
key: ${{ github.job }}-${{ runner.os }}-${{ runner.arch }}
- name: Install Dependencies
@@ -35,7 +35,7 @@
ninja \
pkg-config
- name: Install Python Dependencies
- run: pip3 install fonttools gcovr==5.0
+ run: pip3 install -r .ci/requirements.txt --require-hashes
- name: Setup Meson
run: |
export PKG_CONFIG_PATH="/usr/local/opt/icu4c/lib/pkgconfig:/usr/local/opt/libffi/lib/pkgconfig"
@@ -55,6 +55,6 @@
- name: Generate Coverage
run: ninja -Cbuild coverage-xml
- name: Upload Coverage
- uses: codecov/codecov-action@v3
+ uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # v3.1.4
with:
file: build/meson-logs/coverage.xml
diff --git a/.github/workflows/msvc-ci.yml b/.github/workflows/msvc-ci.yml
index 57ab3af..6aadb81 100644
--- a/.github/workflows/msvc-ci.yml
+++ b/.github/workflows/msvc-ci.yml
@@ -28,33 +28,33 @@
steps:
- name: Checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
- name: Setup Ccache
- uses: hendrikmuhs/ccache-action@v1.2
+ uses: hendrikmuhs/ccache-action@ca3acd2731eef11f1572ccb126356c2f9298d35e # v1.2.9
with:
variant: sccache
key: ${{ github.job }}-${{ matrix.os }}-${{ matrix.ARCH }}
- name: Setup Python
- uses: actions/setup-python@v4
+ uses: actions/setup-python@bd6b4b6205c4dbad673328db7b31b7fab9e241c0 # v4.6.1
with:
python-version: '3.x'
- name: Setup MSVC
- uses: ilammy/msvc-dev-cmd@v1
+ uses: ilammy/msvc-dev-cmd@cec98b9d092141f74527d0afa6feb2af698cfe89 # v1.12.1
with:
arch : ${{ matrix.ARCH }}
- name: Install Python Dependencies
run: |
- pip install --upgrade meson ninja fonttools
+ pip3 install -r .ci/requirements.txt --require-hashes
- name: Setup Meson
run: |
- sccache --version
- meson setup build `
- --wrap-mode=forcefallback `
- --buildtype=release `
- -Dglib=enabled `
- -Dfreetype=enabled `
- -Dgdi=enabled `
- -Ddirectwrite=enabled
+ sccache --version
+ meson setup build `
+ --wrap-mode=forcefallback `
+ --buildtype=release `
+ -Dglib=enabled `
+ -Dfreetype=enabled `
+ -Dgdi=enabled `
+ -Ddirectwrite=enabled
- name: Build
run: meson compile -Cbuild
- name: Test
diff --git a/.github/workflows/msys2-ci.yml b/.github/workflows/msys2-ci.yml
index d825fd0..030aee4 100644
--- a/.github/workflows/msys2-ci.yml
+++ b/.github/workflows/msys2-ci.yml
@@ -33,9 +33,9 @@
shell: msys2 {0}
steps:
- name: Checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
- name: Setup MSYS2
- uses: msys2/setup-msys2@v2
+ uses: msys2/setup-msys2@cf96e00c0aab3788743aaf63b64146f0d383cee9 # v2
with:
msystem: ${{ matrix.MSYSTEM }}
update: true
@@ -56,7 +56,7 @@
mingw-w64-${{ matrix.MSYS2_ARCH }}-python-pip
- name: Install Python Dependencies
run: |
- pip install --upgrade fonttools
+ pip3 install -r .ci/requirements-fonttools.txt --require-hashes
- name: Setup Meson
run: |
meson setup build \
diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml
new file mode 100644
index 0000000..70bac36
--- /dev/null
+++ b/.github/workflows/scorecard.yml
@@ -0,0 +1,64 @@
+# This workflow uses actions that are not certified by GitHub. They are provided
+# by a third-party and are governed by separate terms of service, privacy
+# policy, and support documentation.
+
+name: Scorecard supply-chain security
+on:
+ # For Branch-Protection check. Only the default branch is supported. See
+ # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
+ branch_protection_rule:
+ # To guarantee Maintained check is occasionally updated. See
+ # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
+ schedule:
+ - cron: '19 14 * * 6'
+ push:
+ branches: [ "main" ]
+
+# Declare default permissions as read only.
+permissions: read-all
+
+jobs:
+ analysis:
+ name: Scorecard analysis
+ runs-on: ubuntu-latest
+ permissions:
+ # Needed to upload the results to code-scanning dashboard.
+ security-events: write
+ # Needed to publish results and get a badge (see publish_results below).
+ id-token: write
+
+ steps:
+ - name: "Checkout code"
+ uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
+ with:
+ persist-credentials: false
+
+ - name: "Run analysis"
+ uses: ossf/scorecard-action@08b4669551908b1024bb425080c797723083c031 # v2.2.0
+ with:
+ results_file: results.sarif
+ results_format: sarif
+ # (Optional) Fine-grained PAT token. Uncomment the `repo_token` line below if:
+ # you want to enable the full Branch-Protection check on a *public* repository
+ # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-fine-grained-pat-optional.
+ # repo_token: ${{ secrets.SCORECARD_TOKEN }}
+
+ # - Publish results to OpenSSF REST API for easy access by consumers
+ # - Allows the repository to include the Scorecard badge.
+ # - See https://github.com/ossf/scorecard-action#publishing-results.
+ publish_results: true
+
+ # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
+ # format to the repository Actions tab.
+ - name: "Upload artifact"
+ uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
+ with:
+ name: SARIF file
+ path: results.sarif
+ retention-days: 5
+
+ # Upload the results to GitHub's code scanning dashboard.
+ - name: "Upload to code-scanning"
+ uses: github/codeql-action/upload-sarif@46ed16ded91731b2df79a2893d3aea8e9f03b5c4 # v2.20.3
+ with:
+ sarif_file: results.sarif
diff --git a/METADATA b/METADATA
index 40c676d..b16afdb 100644
--- a/METADATA
+++ b/METADATA
@@ -9,7 +9,7 @@
type: GIT
value: "https://github.com/harfbuzz/harfbuzz"
}
- version: "7.3.0"
+ version: "8.0.1"
license_type: RESTRICTED
license_note: "would be NOTICE save for GPL in:\n"
" m4/ax_code_coverage.m4\n"
@@ -18,7 +18,7 @@
" host CC-BY-NC* content.\n"
last_upgrade_date {
year: 2023
- month: 5
- day: 29
+ month: 7
+ day: 12
}
}
diff --git a/NEWS b/NEWS
index ff35843..1454a21 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,56 @@
+Overview of changes leading to 8.0.1
+Wednesday, July 12, 2023
+====================================
+- Build fix on 32-bit arm.
+
+- More speed optimizations:
+ - 60% speedup in retaingids subsetting SourceHanSans-VF.
+ - 38% speed up in subsetting (beyond-64k) mega-merged Noto.
+ - 16% speed up in retain-gid (used for IFT) subsetting of NotoSansCJKkr.
+
+
+Overview of changes leading to 8.0.0
+Sunday, July 9, 2023
+====================================
+- New, experimental, WebAssembly (WASM) shaper, that provides greater
+ flexibility over OpenType/AAT/Graphite shaping, using WebAssembly embedded
+ inside the font file. Currently WASM shaper is disabled by default and needs
+ to be enabled at build time. For details, see:
+
+ https://github.com/harfbuzz/harfbuzz/blob/main/docs/wasm-shaper.md
+
+ For example fonts making use of the WASM shaper, see:
+
+ https://github.com/simoncozens/wasm-examples
+
+- Improvements to Experimental features introduced in earlier releases:
+ - Support for subsetting beyond-64k and VarComposites fonts.
+ - Support for instancing variable fonts with cubic “glyf” table.
+
+- Many big speed optimizations:
+ - Up to 89% speedup loading variable fonts for shaping.
+ - Up to 88% speedup in small subsets of large (eg. CJK) fonts (both TTF and
+ OTF), essential for Incremental Font Transfer (IFT).
+ - Over 50% speedup in loading Roboto font for shaping.
+ - Up to 40% speed up in loading (sanitizing) complex fonts.
+ - 30% speed up in shaping Gulzar font.
+ - Over 25% speedup in glyph loading Roboto font.
+ - 10% speed up loading glyph shapes in VarComposite Hangul font.
+ - hb-hashmap optimizations & hashing improvements.
+
+- New macro HB_ALWAYS_INLINE. HarfBuzz now inlines functions more aggressively,
+ which results in some speedup at the expense of bigger code size. To disable
+ this feature define the macro to just inline.
+
+- New API:
++HB_CODEPOINT_INVALID
++hb_ot_layout_get_baseline2()
++hb_ot_layout_get_baseline_with_fallback2()
++hb_ot_layout_get_font_extents()
++hb_ot_layout_get_font_extents2()
++hb_subset_input_set_axis_range()
+
+
Overview of changes leading to 7.3.0
Tuesday, May 9, 2023
====================================
diff --git a/README.md b/README.md
index 4202961..87f5755 100644
--- a/README.md
+++ b/README.md
@@ -5,6 +5,8 @@
[](https://www.codacy.com/gh/harfbuzz/harfbuzz/dashboard?utm_source=github.com&utm_medium=referral&utm_content=harfbuzz/harfbuzz&utm_campaign=Badge_Grade)
[](https://codecov.io/gh/harfbuzz/harfbuzz)
[](https://repology.org/project/harfbuzz/versions)
+[](https://securityscorecards.dev/viewer/?uri=github.com/harfbuzz/harfbuzz)
+
# HarfBuzz
diff --git a/README.python.md b/README.python.md
index 7496f04..c945d08 100644
--- a/README.python.md
+++ b/README.python.md
@@ -9,9 +9,13 @@
And then run `meson setup` and make sure that `Introspection` is reported
enabled in output.
+If you are building with Visual Studio, it is recommended that Visual Studio
+2019 or later is used for this build, for the best build experience.
+
Compile and install.
-Make sure you have the installation lib dir in `LD_LIBRARY_PATH`, as needed
+Make sure you have the installation lib dir in `LD_LIBRARY_PATH` (or the
+installation DLL dir in `PATH` for Windows systems), as needed
for the linker to find the library.
Then make sure you also have `GI_TYPELIB_PATH` pointing to the resulting
diff --git a/configure.ac b/configure.ac
index a95006b..3de964b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,6 +1,6 @@
AC_PREREQ([2.64])
AC_INIT([HarfBuzz],
- [7.3.0],
+ [8.0.1],
[https://github.com/harfbuzz/harfbuzz/issues/new],
[harfbuzz],
[http://harfbuzz.org/])
@@ -417,6 +417,28 @@
dnl ===========================================================================
+AC_ARG_WITH(wasm,
+ [AS_HELP_STRING([--with-wasm=@<:@yes/no/auto@:>@],
+ [Use the wasm-micro-runtime library @<:@default=no@:>@])],,
+ [with_wasm=no])
+have_wasm=false
+if test "x$with_wasm" = "xyes" -o "x$with_wasm" = "xauto"; then
+ AC_CHECK_HEADERS(wasm_export.h, have_wasm=true)
+fi
+if test "x$with_wasm" = "xyes" -a "x$have_wasm" != "xtrue"; then
+ AC_MSG_ERROR([wasm support requested but not found])
+fi
+if $have_wasm; then
+ WASM_CFLAGS=
+ WASM_LIBS="-liwasm"
+ AC_SUBST(WASM_CFLAGS)
+ AC_SUBST(WASM_LIBS)
+ AC_DEFINE(HAVE_WASM, 1, [Have wasm-micro-runtime library])
+fi
+AM_CONDITIONAL(HAVE_WASM, $have_wasm)
+
+dnl ===========================================================================
+
AC_CONFIG_FILES([
Makefile
src/Makefile
@@ -478,6 +500,7 @@
DirectWrite: ${have_directwrite}
GDI: ${have_gdi}
Uniscribe: ${have_uniscribe}
+ WebAssembly: ${have_wasm}
Other features:
Documentation: ${enable_gtk_doc}
diff --git a/docs/harfbuzz-docs.xml b/docs/harfbuzz-docs.xml
index 3cb1be0..952705a 100644
--- a/docs/harfbuzz-docs.xml
+++ b/docs/harfbuzz-docs.xml
@@ -120,6 +120,7 @@
<index id="api-index-full"><title>API Index</title><xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include></index>
<index id="deprecated-api-index"><title>Index of deprecated API</title><xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include></index>
+ <index id="api-index-8-0-0"><title>Index of new symbols in 8.0.0</title><xi:include href="xml/api-index-8.0.0.xml"><xi:fallback /></xi:include></index>
<index id="api-index-7-3-0"><title>Index of new symbols in 7.3.0</title><xi:include href="xml/api-index-7.3.0.xml"><xi:fallback /></xi:include></index>
<index id="api-index-7-1-0"><title>Index of new symbols in 7.1.0</title><xi:include href="xml/api-index-7.1.0.xml"><xi:fallback /></xi:include></index>
<index id="api-index-7-0-0"><title>Index of new symbols in 7.0.0</title><xi:include href="xml/api-index-7.0.0.xml"><xi:fallback /></xi:include></index>
diff --git a/docs/harfbuzz-sections.txt b/docs/harfbuzz-sections.txt
index dff9bac..fd07b81 100644
--- a/docs/harfbuzz-sections.txt
+++ b/docs/harfbuzz-sections.txt
@@ -1,11 +1,4 @@
<SECTION>
-<SUBSECTION Private>
-HB_H_IN
-HB_OT_H_IN
-HB_AAT_H_IN
-</SECTION>
-
-<SECTION>
<FILE>hb-aat-layout</FILE>
HB_AAT_LAYOUT_NO_SELECTOR_INDEX
hb_aat_layout_feature_type_t
@@ -151,6 +144,7 @@
hb_variation_to_string
hb_bool_t
hb_codepoint_t
+HB_CODEPOINT_INVALID
hb_destroy_func_t
hb_direction_t
hb_language_t
@@ -183,6 +177,10 @@
HB_EXTERN
HB_DEPRECATED
HB_DEPRECATED_FOR
+<SUBSECTION Private>
+HB_H_IN
+HB_OT_H_IN
+HB_AAT_H_IN
</SECTION>
<SECTION>
@@ -197,6 +195,7 @@
HB_HAS_GRAPHITE
HB_HAS_ICU
HB_HAS_UNISCRIBE
+HB_HAS_WASM
</SECTION>
<SECTION>
@@ -320,6 +319,9 @@
hb_unicode_funcs_set_decompose_compatibility_func
HB_UNICODE_COMBINING_CLASS_CCC133
hb_font_funcs_set_glyph_v_kerning_func
+hb_font_get_glyph_shape
+hb_font_get_glyph_shape_func_t
+hb_font_funcs_set_glyph_shape_func
hb_font_get_glyph_v_kerning
hb_font_get_glyph_v_kerning_func_t
</SECTION>
@@ -404,7 +406,6 @@
hb_font_get_glyph_v_origin
hb_font_get_glyph_origin_for_direction
hb_font_get_glyph_name
-hb_font_get_glyph_shape
hb_font_draw_glyph
hb_font_paint_glyph
hb_font_get_nominal_glyph
@@ -472,8 +473,6 @@
hb_font_funcs_set_glyph_v_origin_func
hb_font_get_glyph_name_func_t
hb_font_funcs_set_glyph_name_func
-hb_font_get_glyph_shape_func_t
-hb_font_funcs_set_glyph_shape_func
hb_font_draw_glyph_func_t
hb_font_funcs_set_draw_glyph_func
hb_font_paint_glyph_func_t
@@ -625,9 +624,13 @@
hb_ot_layout_feature_get_name_ids
hb_ot_layout_feature_with_variations_get_lookups
hb_ot_layout_get_attach_points
+hb_ot_layout_get_font_extents
+hb_ot_layout_get_font_extents2
hb_ot_layout_get_horizontal_baseline_tag_for_script
hb_ot_layout_get_baseline
+hb_ot_layout_get_baseline2
hb_ot_layout_get_baseline_with_fallback
+hb_ot_layout_get_baseline_with_fallback2
hb_ot_layout_get_glyph_class
hb_ot_layout_get_glyphs_in_class
hb_ot_layout_get_ligature_carets
@@ -898,6 +901,7 @@
hb_object_t
hb_subset_repack_or_fail
hb_subset_input_override_name_table
+hb_subset_input_set_axis_range
</SECTION>
<SECTION>
diff --git a/docs/meson.build b/docs/meson.build
index faf558a..45ca93a 100644
--- a/docs/meson.build
+++ b/docs/meson.build
@@ -41,6 +41,7 @@
'hb-gobject-enums.h',
'hb-gobject-enums-tmp.h',
'hb-gobject-structs.h',
+ 'hb-wasm-api.h',
]
gnome.gtkdoc('harfbuzz',
diff --git a/docs/wasm-shaper.md b/docs/wasm-shaper.md
new file mode 100644
index 0000000..5343aa3
--- /dev/null
+++ b/docs/wasm-shaper.md
@@ -0,0 +1,547 @@
+# The web assembly shaper
+
+If the standard OpenType shaping engine doesn't give you enough flexibility, Harfbuzz allows you to write your own shaping engine in WebAssembly and embed it into your font! Any font which contains a `Wasm` table will be passed to the WebAssembly shaper.
+
+## What you can and can't do: the WASM shaper's role in shaping
+
+The Harfbuzz shaping engine, unlike its counterparts CoreText and DirectWrite, is only responsible for a small part of the text rendering process. Specifically, Harfbuzz is purely responsible for *shaping*; although Harfbuzz does have APIs for accessing glyph outlines, typically other libraries in the free software text rendering stack are responsible for text segmentation into runs, outline scaling and rasterizing, setting text on lines, and so on.
+
+Harfbuzz is therefore restricted to turning a buffer of codepoints for a segmented run of the same script, language, font, and variation settings, into glyphs and positioning them. This is also all that you can do with the WASM shaper; you can influence the process of mapping a string of characters into an array of glyphs, you can determine how those glyphs are positioned and their advance widths, but you cannot manipulate outlines, variations, line breaks, or affect text layout between texts of different font, variation, language, script or OpenType feature selection.
+
+## The WASM shaper interface
+
+The WASM code inside a font is expected to export a function called `shape` which takes five int32 arguments and returns an int32 status value. (Zero for failure, any other value for success.) Three of the five arguments are tokens which can be passed to the API functions exported to your WASM code by the host shaping engine:
+
+* A *shape plan* token, which can largely be ignored.
+* A *font* token.
+* A *buffer* token.
+* A *feature* array.
+* The number of features.
+
+The general goal of WASM shaping involves receiving and manipulating a *buffer contents* structure, which is an array of *infos* and *positions* (as defined below). Initially this buffer will represent an input string in Unicode codepoints. By the end of your `shape` function, it should represent a set of glyph IDs and their positions. (User-supplied WASM code will manipulate the buffer through *buffer tokens*; the `buffer_copy_contents` and `buffer_set_contents` API functions, defined below, use these tokens to exchange buffer information with the host shaping engine.)
+
+* The `buffer_contents_t` structure
+
+| type | field | description|
+| - | - | - |
+| uint32 | length | Number of items (characters or glyphs) in the buffer
+| glyph_info_t | infos | An array of `length` glyph infos |
+| glyph_position_t | positions | An array of `length` glyph positions |
+
+* The `glyph_info_t` structure
+
+| type | field | description|
+| - | - | - |
+| uint32 | codepoint | (On input) A Unicode codepoint. (On output) A glyph ID. |
+| uint32 | mask | Unused in WASM; can be user-defined |
+| uint32 | cluster | Index of start of this graphical cluster in input string |
+| uint32 | var1 | Reserved |
+| uint32 | var2 | Reserved |
+
+The `cluster` field is used to glyphs in the output glyph stream back to characters in the input Unicode sequence for hit testing, cursor positioning, etc. It must be set to a monotonically increasing value across the buffer.
+
+* The `glyph_position_t` structure
+
+| type | field | description|
+| - | - | - |
+| int32 | x_advance | X advance of the glyph |
+| int32 | y_advance | Y advance of the glyph |
+| int32 | x_offset | X offset of the glyph |
+| int32 | y_offset | Y offset of the glyph |
+| uint32 | var | Reserved |
+
+* The `feature_t` array
+
+To communicate user-selected OpenType features to the user-defined WASM shaper, the host shaping engine passes an array of feature structures:
+
+| type | field | description|
+| - | - | - |
+| uint32 | tag | Byte-encoded feature tag |
+| uint32 | value | Value: 0=off, 1=on, other values used for alternate selection |
+| uint32 | start | Index into the input string representing start of the active region for this feature selection (0=start of string) |
+| uint32 | end | Index into the input string representing end of the active region for this feature selection (-1=end of string) |
+
+## API functions available
+
+To assist the shaping code in mapping codepoints to glyphs, the WASM shaper exports the following functions. Note that these are the low level API functions; WASM authors may prefer to use higher-level abstractions around these functions, such as the `harfbuzz-wasm` Rust crate provided by Harfbuzz.
+
+### Sub-shaping
+
+* `shape_with`
+
+```C
+bool shape_with(
+ uint32 font_token,
+ uint32 buffer_token,
+ feature_t* features,
+ uint32 num_features,
+ char* shaper
+)
+```
+
+Run another shaping engine's shaping process on the given font and buffer. The only shaping engine guaranteed to be available is `ot`, the OpenType shaper, but others may also be available. This allows the WASM author to process a buffer "normally", before further manipulating it.
+
+### Buffer access
+
+* `buffer_copy_contents`
+
+```C
+bool buffer_copy_contents(
+ uint32 buffer_token,
+ buffer_contents_t* buffer_contents
+)
+```
+
+Retrieves the contents of the host shaping engine's buffer into the `buffer_contents` structure. This should typically be called at the beginning of shaping.
+
+* `buffer_set_contents`
+
+```C
+bool buffer_set_contents(
+ uint32 buffer_token,
+ buffer_contents_t* buffer_contents
+)
+```
+
+Copy the `buffer_contents` structure back into the host shaping engine's buffer. This should typically be called at the end of shaping.
+
+* `buffer_contents_free`
+
+```C
+bool buffer_contents_free(buffer_contents_t* buffer_contents)
+```
+
+Releases the memory taken up by the buffer contents structure.
+
+* `buffer_contents_realloc`
+
+```C
+bool buffer_contents_realloc(
+ buffer_contents_t* buffer_contents,
+ uint32 size
+)
+```
+
+Requests that the buffer contents structure be resized to the given size.
+
+* `buffer_get_direction`
+
+```C
+uint32 buffer_get_direction(uint32 buffer_token)
+```
+
+Returns the buffer's direction:
+
+* 0 = invalid
+* 4 = left to right
+* 5 = right to left
+* 6 = top to bottom
+* 7 = bottom to top
+
+* `buffer_get_script`
+
+```C
+uint32 buffer_get_script(uint32 buffer_token)
+```
+
+Returns the byte-encoded OpenType script tag of the buffer.
+
+* `buffer_reverse`
+
+```C
+void buffer_reverse(uint32 buffer_token)
+```
+
+Reverses the order of items in the buffer.
+
+* `buffer_reverse_clusters`
+
+```C
+void buffer_reverse_clusters(uint32 buffer_token)
+```
+
+Reverses the order of items in the buffer while keeping items of the same cluster together.
+
+## Font handling functions
+
+(In the following functions, a *font* is a specific instantiation of a *face* at a particular scale factor and variation position.)
+
+* `font_create`
+
+```C
+uint32 font_create(uint32 face_token)
+```
+
+Returns a new *font token* from the given *face token*.
+
+* `font_get_face`
+
+```C
+uint32 font_get_face(uint32 font_token)
+```
+
+Creates a new *face token* from the given *font token*.
+
+* `font_get_scale`
+
+```C
+void font_get_scale(
+ uint32 font_token,
+ int32* x_scale,
+ int32* y_scale
+)
+```
+
+Returns the scale of the current font.
+
+* `font_get_glyph`
+
+```C
+uint32 font_get_glyph(
+ uint32 font_token,
+ uint32 codepoint,
+ uint32 variation_selector
+)
+```
+
+Returns the nominal glyph ID for the given codepoint, using the `cmap` table of the font to map Unicode codepoint (and variation selector) to glyph ID.
+
+* `font_get_glyph_h_advance`/`font_get_glyph_v_advance`
+
+```C
+uint32 font_get_glyph_h_advance(uint32 font_token, uint32 glyph_id)
+uint32 font_get_glyph_v_advance(uint32 font_token, uint32 glyph_id)
+```
+
+Returns the default horizontal and vertical advance respectively for the given glyph ID the current scale and variations settings.
+
+* `font_get_glyph_extents`
+
+```C
+typedef struct
+{
+ uint32 x_bearing;
+ uint32 y_bearing;
+ uint32 width;
+ uint32 height;
+} glyph_extents_t;
+
+bool font_get_glyph_extents(
+ uint32 font_token,
+ uint32 glyph_id,
+ glyph_extents_t* extents
+)
+```
+
+Returns the glyph's extents for the given glyph ID at current scale and variation settings.
+
+* `font_glyph_to_string`
+
+```C
+void font_glyph_to_string(
+ uint32 font_token,
+ uint32 glyph_id,
+ char* string,
+ uint32 size
+)
+```
+
+Copies the name of the given glyph, or, if no name is available, a string of the form `gXXXX` into the given string.
+
+* `font_copy_glyph_outline`
+
+```C
+typedef struct
+{
+ float x;
+ float y;
+ uint32_t type;
+} glyph_outline_point_t;
+
+typedef struct
+{
+ uint32_t n_points;
+ glyph_outline_point_t* points;
+ uint32_t n_contours;
+ uint32_t* contours;
+} glyph_outline_t;
+
+bool font_copy_glyph_outline(
+ uint32 font_token,
+ uint32 glyph_id,
+ glyph_outline_t* outline
+);
+```
+
+Copies the outline of the given glyph ID, at current scale and variation settings, into the outline structure provided. The outline structure returns an array of points (specifying coordinates and whether the point is oncurve or offcurve) and an array of indexes into the points array representing the end of each contour, similar to the `glyf` table structure.
+
+* `font_copy_coords`/`font_set_coords`
+
+```C
+typedef struct
+{
+ uint32 length;
+ int32* coords;
+} coords_t;
+
+bool font_copy_coords(uint32 font_token, &coords_t coords);
+bool font_set_coords(uint32 font_token, &coords_t coords);
+```
+
+`font_copy_coords` copies the font's variation coordinates into the given structure; the resulting structure has `length` equal to the number of variation axes, with each member of the `coords` array being a F2DOT14 encoding of the normalized variation value.
+
+`font_set_coords` sets the font's variation coordinates. Because the WASM shaper is only responsible for shaping and positioning, not outline drawing, the user should *not* expect this to affect the rendered outlines; the function is only useful in very limited circumstances, such as when instantiating a second variable font and sub-shaping a buffer using this new font.
+
+## Face handling functions
+
+* `face_create`
+
+```C
+typedef struct
+{
+ uint32_t length;
+ char* data;
+} blob_t;
+
+uint32 font_get_face(blob_t* blob)
+```
+
+Creates a new *face token* from the given binary data.
+
+* `face_copy_table`
+
+```C
+void face_copy_table(uint32 face_token, uint32 tag, blob_t* blob)
+```
+
+Copies the binary data in the OpenType table referenced by `tag` into the supplied `blob` structure.
+
+* `face_get_upem`
+
+```C
+uint32 font_get_upem(uint32 face_token)
+```
+
+Returns the units-per-em of the font face.
+
+### Other functions
+
+* `blob_free`
+
+```C
+void blob_free(blob_t* blob)
+```
+
+Frees the memory allocated to a blob structure.
+
+* `glyph_outline_free`
+
+```C
+void glyph_outline_free(glyph_outline_t* glyph_outline)
+```
+
+Frees the memory allocated to a glyph outline structure.
+
+* `script_get_horizontal_direction`
+
+```C
+uint32 script_get_horizontal_direction(uint32 tag)
+```
+
+Returns the horizontal direction for the given ISO 15924 script tag. For return values, see `buffer_get_direction` above.
+
+* `debugprint` / `debugprint1` ... `debugprint4`
+
+```C
+void debugprint(char* str)
+void debugprint1(char* str, int32 arg1)
+void debugprint2(char* str, int32 arg1, int32 arg2)
+void debugprint3(char* str, int32 arg1, int32 arg2, int32 arg3)
+void debugprint3(
+ char* str,
+ int32 arg1,
+ int32 arg2,
+ int32 arg3,
+ int32 arg4
+)
+```
+
+Produces a debugging message in the host shaper's log output; the variants `debugprint1` ... `debugprint4` suffix the message with a comma-separated list of the integer arguments.
+
+## Enabling the WASM shaper when building Harfbuzz
+
+First, you will need the `wasm-micro-runtime` library installed on your computer. Download `wasm-micro-runtime` from [its GitHub repository](https://github.com/bytecodealliance/wasm-micro-runtime/tree/main); then follow [the instructions for building](https://github.com/bytecodealliance/wasm-micro-runtime/blob/main/product-mini/README.md), except add the `-DWAMR_BUILD_REF_TYPES=1` flag to the `cmake` line. (You may want to enable "fast JIT".) Follow this with a `make install`.
+
+So, for example:
+
+```
+$ cd product-mini/platforms/darwin
+$ mkdir build
+$ cd build
+$ cmake .. -DWAMR_BUILD_REF_TYPES=1 -DWAMR_BUILD_FAST_JIT=1
+$ make
+$ make install
+```
+
+(If you don't want to install `wasm-micro-runtime` globally, you can copy `libiwasm.*` and `libvmlib.a` into a directory that your compiler can see when building Harfbuzz.)
+
+Once `wasm-micro-runtime` is installed, to enable the WASM shaper, you need to add the string `-Dwasm=enabled` to your meson build line. For example:
+
+```
+$ meson setup build -Dwasm=enabled
+...
+ Additional shapers
+ Graphite2 : NO
+ WebAssembly (experimental): YES
+...
+$ meson compile -C build
+```
+
+## How to write a shaping engine in Rust
+
+You may write shaping engines in any language supported by WASM, by conforming to the API described above, but Rust is particularly easy, and we have one of those high-level interface wrappers which makes the process easier. Here are the steps to create an example shaping engine in Rust: (These examples can also be found in `src/wasm/sample/rust`)
+
+* First, install wasm-pack, which helps us to generate optimized WASM files. It writes some Javascript bridge code that we don't need, but it makes the build and deployment process much easier:
+
+```
+$ cargo install wasm-pack
+```
+
+* Now let's create a new library:
+
+```
+$ cargo new --lib hello-wasm
+```
+
+* We need the target to be a dynamic library, and we're going to use `bindgen` to export our Rust function to WASM, so let's put these lines in the `Cargo.toml`. The Harfbuzz sources contain a Rust crate which makes it easy to create the shaper, so we'll specify that as a dependency as well:
+
+```toml
+[lib]
+crate-type = ["cdylib"]
+[dependencies]
+wasm-bindgen = "0.2"
+harfbuzz-wasm = { path = "your-harfbuzz-source/src/wasm/rust/harfbuzz-wasm"}
+```
+
+*
+* And now we'll create our shaper code. In `src/lib.rs`:
+
+```rust
+use wasm_bindgen::prelude::*;
+
+#[wasm_bindgen]
+pub fn shape(_shape_plan:u32, font_ref: u32, buf_ref: u32, _features: u32, _num_features: u32) -> i32 {
+ 1 // success!
+}
+```
+
+This exports a shaping function which takes four arguments, tokens representing the shaping plan, the font and the buffer, and returns a status value. We can pass these tokens back to Harfbuzz in order to use its native functions on the font and buffer objects. More on native functions later - let's get this shaper compiled and added into a font:
+
+* To compile the shaper, run `wasm-pack build --target nodejs`:
+
+```
+INFO]: 🎯 Checking for the Wasm target...
+[INFO]: 🌀 Compiling to Wasm...
+ Compiling hello-wasm v0.1.0 (...)
+ Finished release [optimized] target(s) in 0.20s
+[WARN]: ⚠️ origin crate has no README
+[INFO]: ⬇️ Installing wasm-bindgen...
+[INFO]: Optimizing wasm binaries with `wasm-opt`...
+[INFO]: Optional fields missing from Cargo.toml: 'description', 'repository', and 'license'. These are not necessary, but recommended
+[INFO]: ✨ Done in 0.40s
+```
+
+You'll find the output WASM file in `pkg/hello_wasm_bg.wasm`
+
+* Now we need to get it into a font.
+
+We provide a utility to do this called `addTable.py` in the `src/` directory:
+
+```
+% python3 ~/harfbuzz/src/addTable.py test.ttf test-wasm.ttf pkg/hello_wasm_bg.wasm
+```
+
+And now we can run it!
+
+```
+% hb-shape test-wasm.ttf abc --shapers=wasm
+[cent=0|sterling=1|fraction=2]
+```
+
+(The `--shapers=wasm` isn't necessary, as any font with a `Wasm` table will be sent to the WASM shaper if it's enabled, but it proves the point.)
+
+Congratulations! Our shaper did nothing, but in Rust! Now let's do something - it's time for the Hello World of WASM shaping.
+
+* To say hello world, we're going to have to use a native function.
+
+In debugging builds of Harfbuzz, we can print some output from the web assembly module to the host's standard output using the `debug` function. To make this easier, we've got the `harfbuzz-wasm` crate:
+
+```rust
+use harfbuzz_wasm::debug;
+
+#[wasm_bindgen]
+pub fn shape(_shape_plan:u32, _font_ref: u32, _buf_ref: u32, _features: u32, _num_features: u32) -> i32 {
+ debug("Hello from Rust!\n");
+ 1
+}
+```
+
+With this compiled into a WASM module, and installed into our font again, finally our fonts can talk to us!
+
+```
+$ hb-shape test-wasm.ttf abc
+Hello from Rust!
+[cent=0|sterling=1|fraction=2]
+```
+
+Now let's start to do some actual, you know, *shaping*. The first thing a shaping engine normally does is (a) map the items in the buffer from Unicode codepoints into glyphs in the font, and (b) set the advance width of the buffer items to the default advance width for those glyphs. We're going to need to interrogate the font for this information, and write back to the buffer. Harfbuzz provides us with opaque pointers to the memory for the font and buffer, but we can turn those into useful Rust structures using the `harfbuzz-wasm` crate again:
+
+```rust
+use wasm_bindgen::prelude::*;
+use harfbuzz_wasm::{Font, GlyphBuffer};
+
+#[wasm_bindgen]
+pub fn shape(_shape_plan:u32, font_ref: u32, buf_ref: u32, _features: u32, _num_features: u32) -> i32 {
+ let font = Font::from_ref(font_ref);
+ let mut buffer = GlyphBuffer::from_ref(buf_ref);
+ for mut item in buffer.glyphs.iter_mut() {
+ // Map character to glyph
+ item.codepoint = font.get_glyph(codepoint, 0);
+ // Set advance width
+ item.h_advance = font.get_glyph_h_advance(item.codepoint);
+ }
+ 1
+}
+```
+
+The `GlyphBuffer`, unlike in Harfbuzz, combines positioning and information in a single structure, to save you having to zip and unzip all the time. It also takes care of marshalling the buffer back to Harfbuzz-land; when a GlyphBuffer is dropped, it writes its contents back through the reference into Harfbuzz's address space. (If you want a different representation of buffer items, you can have one: `GlyphBuffer` is implemented as a `Buffer<Glyph>`, and if you make your own struct which implements the `BufferItem` trait, you can make a buffer out of that instead.)
+
+One easy way to write your own shapers is to make use of OpenType shaping for the majority of your shaping work, and then make changes to the pre-shaped buffer afterwards. You can do this using the `Font.shape_with` method. Run this on a buffer reference, and then construct your `GlyphBuffer` object afterwards:
+
+```rust
+use harfbuzz_wasm::{Font, GlyphBuffer};
+use tiny_rng::{Rand, Rng};
+use wasm_bindgen::prelude::*;
+
+#[wasm_bindgen]
+pub fn shape(_shape_plan:u32, font_ref: u32, buf_ref: u32, _features: u32, _num_features: u32) -> i32 {
+ let mut rng = Rng::from_seed(123456);
+
+ // Use the default OpenType shaper
+ let font = Font::from_ref(font_ref);
+ font.shape_with(buf_ref, "ot");
+
+ // Now we have a buffer with glyph ids, advance widths etc.
+ // already filled in.
+ let mut buffer = GlyphBuffer::from_ref(buf_ref);
+ for mut item in buffer.glyphs.iter_mut() {
+ // Randomize it!
+ item.x_offset = ((rng.rand_u32() as i32) >> 24) - 120;
+ item.y_offset = ((rng.rand_u32() as i32) >> 24) - 120;
+ }
+
+ 1
+}
+```
+
+See the documentation for the `harfbuzz-wasm` crate for all the other
diff --git a/meson.build b/meson.build
index 512c690..c3062c7 100644
--- a/meson.build
+++ b/meson.build
@@ -1,6 +1,6 @@
project('harfbuzz', 'c', 'cpp',
meson_version: '>= 0.55.0',
- version: '7.3.0',
+ version: '8.0.1',
default_options: [
'cpp_eh=none', # Just to support msvc, we are passing -fno-exceptions also anyway
'cpp_rtti=false', # Just to support msvc, we are passing -fno-rtti also anyway
@@ -115,13 +115,15 @@
gobject_dep = dependency('gobject-2.0', required: get_option('gobject'))
graphite2_dep = dependency('graphite2', required: get_option('graphite2'))
graphite_dep = dependency('graphite2', required: get_option('graphite'))
+wasm_dep = cpp.find_library('iwasm', required: get_option('wasm'))
+# How to check whether iwasm was built, and hence requires, LLVM?
+#llvm_dep = cpp.find_library('LLVM-15', required: get_option('wasm'))
if meson.version().version_compare('>=0.60.0')
# pkg-config: icu-uc, cmake: ICU but with components
icu_dep = dependency('icu-uc', 'ICU',
components: 'uc',
required: get_option('icu'),
- default_options: ['harfbuzz=disabled'],
allow_fallback: true)
else
# painful hack to handle multiple dependencies but also respect options
@@ -230,6 +232,11 @@
conf.set('HAVE_CHAFA', 1)
endif
+if wasm_dep.found()
+ conf.set('HAVE_WASM', 1)
+ conf.set('HB_WASM_MODULE_DIR', '"'+get_option('prefix')+'/'+get_option('libdir')+'/harfbuzz/wasm"')
+endif
+
if graphite2_dep.found() or graphite_dep.found()
conf.set('HAVE_GRAPHITE2', 1)
endif
@@ -419,10 +426,11 @@
},
'Additional shapers':
{'Graphite2': conf.get('HAVE_GRAPHITE2', 0) == 1,
+ 'WebAssembly (experimental)': conf.get('HAVE_WASM', 0) == 1,
},
'Platform shapers (not normally needed)':
{'CoreText': conf.get('HAVE_CORETEXT', 0) == 1,
- 'DirectWrite': conf.get('HAVE_DIRECTWRITE', 0) == 1,
+ 'DirectWrite (experimental)': conf.get('HAVE_DIRECTWRITE', 0) == 1,
'GDI/Uniscribe': (conf.get('HAVE_GDI', 0) == 1) and (conf.get('HAVE_UNISCRIBE', 0) == 1),
},
'Other features':
diff --git a/meson_options.txt b/meson_options.txt
index 9438202..ace50eb 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -21,6 +21,8 @@
description: 'Enable DirectWrite shaper backend on Windows (experimental)')
option('coretext', type: 'feature', value: 'disabled',
description: 'Enable CoreText shaper backend on macOS')
+option('wasm', type: 'feature', value: 'disabled',
+ description: 'Enable WebAssembly shaper backend (experimental)')
# Common feature options
option('tests', type: 'feature', value: 'enabled', yield: true,
diff --git a/perf/benchmark-font.cc b/perf/benchmark-font.cc
index cdf33c3..dfe1fa2 100644
--- a/perf/benchmark-font.cc
+++ b/perf/benchmark-font.cc
@@ -21,7 +21,8 @@
const char *font_path;
} default_tests[] =
{
- {true , SUBSET_FONT_BASE_PATH "Roboto-Regular.ttf"},
+ {false, SUBSET_FONT_BASE_PATH "Roboto-Regular.ttf"},
+ {true , SUBSET_FONT_BASE_PATH "RobotoFlex-Variable.ttf"},
{false, SUBSET_FONT_BASE_PATH "SourceSansPro-Regular.otf"},
{true , SUBSET_FONT_BASE_PATH "AdobeVFPrototype.otf"},
{true , SUBSET_FONT_BASE_PATH "SourceSerifVariable-Roman.ttf"},
@@ -41,22 +42,43 @@
glyph_h_advances,
glyph_extents,
draw_glyph,
+ load_face_and_shape,
};
static void
-_hb_move_to (hb_draw_funcs_t *, void *, hb_draw_state_t *, float, float, void *) {}
+_hb_move_to (hb_draw_funcs_t *, void *draw_data, hb_draw_state_t *, float x, float y, void *)
+{
+ float &i = * (float *) draw_data;
+ i += x + y;
+}
static void
-_hb_line_to (hb_draw_funcs_t *, void *, hb_draw_state_t *, float, float, void *) {}
-
-//static void
-//_hb_quadratic_to (hb_draw_funcs_t *, void *, hb_draw_state_t *, float, float, float, float, void *) {}
+_hb_line_to (hb_draw_funcs_t *, void *draw_data, hb_draw_state_t *, float x, float y, void *)
+{
+ float &i = * (float *) draw_data;
+ i += x + y;
+}
static void
-_hb_cubic_to (hb_draw_funcs_t *, void *, hb_draw_state_t *, float, float, float, float, float, float, void *) {}
+_hb_quadratic_to (hb_draw_funcs_t *, void *draw_data, hb_draw_state_t *, float cx, float cy, float x, float y, void *)
+{
+ float &i = * (float *) draw_data;
+ i += cx + cy + x + y;
+}
static void
-_hb_close_path (hb_draw_funcs_t *, void *, hb_draw_state_t *, void *) {}
+_hb_cubic_to (hb_draw_funcs_t *, void *draw_data, hb_draw_state_t *, float cx1, float cy1, float cx2, float cy2, float x, float y, void *)
+{
+ float &i = * (float *) draw_data;
+ i += cx1 + cy1 + cx2 + cy2 + x + y;
+}
+
+static void
+_hb_close_path (hb_draw_funcs_t *, void *draw_data, hb_draw_state_t *, void *)
+{
+ float &i = * (float *) draw_data;
+ i += 1.0;
+}
static hb_draw_funcs_t *
_draw_funcs_create (void)
@@ -64,7 +86,7 @@
hb_draw_funcs_t *draw_funcs = hb_draw_funcs_create ();
hb_draw_funcs_set_move_to_func (draw_funcs, _hb_move_to, nullptr, nullptr);
hb_draw_funcs_set_line_to_func (draw_funcs, _hb_line_to, nullptr, nullptr);
- //hb_draw_funcs_set_quadratic_to_func (draw_funcs, _hb_quadratic_to, nullptr, nullptr);
+ hb_draw_funcs_set_quadratic_to_func (draw_funcs, _hb_quadratic_to, nullptr, nullptr);
hb_draw_funcs_set_cubic_to_func (draw_funcs, _hb_cubic_to, nullptr, nullptr);
hb_draw_funcs_set_close_path_func (draw_funcs, _hb_close_path, nullptr, nullptr);
return draw_funcs;
@@ -162,10 +184,35 @@
{
hb_draw_funcs_t *draw_funcs = _draw_funcs_create ();
for (auto _ : state)
+ {
+ float i = 0;
for (unsigned gid = 0; gid < num_glyphs; ++gid)
- hb_font_draw_glyph (font, gid, draw_funcs, nullptr);
- break;
+ hb_font_draw_glyph (font, gid, draw_funcs, &i);
+ }
hb_draw_funcs_destroy (draw_funcs);
+ break;
+ }
+ case load_face_and_shape:
+ {
+ for (auto _ : state)
+ {
+ hb_blob_t *blob = hb_blob_create_from_file_or_fail (test_input.font_path);
+ assert (blob);
+ hb_face_t *face = hb_face_create (blob, 0);
+ hb_blob_destroy (blob);
+ hb_font_t *font = hb_font_create (face);
+ hb_face_destroy (face);
+
+ hb_buffer_t *buffer = hb_buffer_create ();
+ hb_buffer_add_utf8 (buffer, " ", -1, 0, -1);
+ hb_buffer_guess_segment_properties (buffer);
+
+ hb_shape (font, buffer, nullptr, 0);
+
+ hb_buffer_destroy (buffer);
+ hb_font_destroy (font);
+ }
+ break;
}
}
@@ -234,6 +281,7 @@
TEST_OPERATION (glyph_h_advances, benchmark::kMicrosecond);
TEST_OPERATION (glyph_extents, benchmark::kMicrosecond);
TEST_OPERATION (draw_glyph, benchmark::kMicrosecond);
+ TEST_OPERATION (load_face_and_shape, benchmark::kMicrosecond);
#undef TEST_OPERATION
diff --git a/perf/benchmark-map.cc b/perf/benchmark-map.cc
index f278a0c..b2f6dae 100644
--- a/perf/benchmark-map.cc
+++ b/perf/benchmark-map.cc
@@ -7,16 +7,23 @@
#include <cstdlib>
#include "hb.h"
-void RandomMap(unsigned size, hb_map_t* out) {
+void RandomMap(unsigned size, hb_map_t* out, hb_set_t* key_sample) {
hb_map_clear(out);
+ unsigned sample_denom = 1;
+ if (size > 10000)
+ sample_denom = size / 10000;
+
srand(size);
for (unsigned i = 0; i < size; i++) {
while (true) {
hb_codepoint_t next = rand();
if (hb_map_has (out, next)) continue;
- hb_map_set (out, next, rand ());
+ hb_codepoint_t val = rand();
+ if (key_sample && val % sample_denom == 0)
+ hb_set_add (key_sample, next);
+ hb_map_set (out, next, val);
break;
}
}
@@ -27,16 +34,20 @@
unsigned map_size = state.range(0);
hb_map_t* original = hb_map_create ();
- RandomMap(map_size, original);
+ RandomMap(map_size, original, nullptr);
assert(hb_map_get_population(original) == map_size);
- auto needle = map_size / 2;
- auto v = 0;
+ unsigned mask = 1;
+ while (mask < map_size)
+ mask <<= 1;
+ mask--;
+
+ auto needle = 0u;
for (auto _ : state) {
// TODO(garretrieger): create a copy of the original map.
// Needs a hb_map_copy(..) in public api.
- hb_map_set (original, needle++, v++);
+ hb_map_set (original, needle++ & mask, 1);
}
hb_map_destroy(original);
@@ -44,12 +55,12 @@
BENCHMARK(BM_MapInsert)
->Range(1 << 4, 1 << 20);
-/* Single value lookup on map of various sizes. */
-static void BM_MapLookup(benchmark::State& state) {
+/* Single value lookup on map of various sizes where the key is not present. */
+static void BM_MapLookupMiss(benchmark::State& state) {
unsigned map_size = state.range(0);
hb_map_t* original = hb_map_create ();
- RandomMap(map_size, original);
+ RandomMap(map_size, original, nullptr);
assert(hb_map_get_population(original) == map_size);
auto needle = map_size / 2;
@@ -61,7 +72,38 @@
hb_map_destroy(original);
}
-BENCHMARK(BM_MapLookup)
+BENCHMARK(BM_MapLookupMiss)
+ ->Range(1 << 4, 1 << 20); // Map size
+
+/* Single value lookup on map of various sizes. */
+static void BM_MapLookupHit(benchmark::State& state) {
+ unsigned map_size = state.range(0);
+
+ hb_map_t* original = hb_map_create ();
+ hb_set_t* key_set = hb_set_create ();
+ RandomMap(map_size, original, key_set);
+ assert(hb_map_get_population(original) == map_size);
+
+ unsigned num_keys = hb_set_get_population (key_set);
+ hb_codepoint_t* key_array =
+ (hb_codepoint_t*) calloc (num_keys, sizeof(hb_codepoint_t));
+
+ hb_codepoint_t cp = HB_SET_VALUE_INVALID;
+ unsigned i = 0;
+ while (hb_set_next (key_set, &cp))
+ key_array[i++] = cp;
+
+ i = 0;
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(
+ hb_map_get (original, key_array[i++ % num_keys]));
+ }
+
+ hb_set_destroy (key_set);
+ free (key_array);
+ hb_map_destroy(original);
+}
+BENCHMARK(BM_MapLookupHit)
->Range(1 << 4, 1 << 20); // Map size
diff --git a/perf/benchmark-subset.cc b/perf/benchmark-subset.cc
index 9b51b79..9f9ec56 100644
--- a/perf/benchmark-subset.cc
+++ b/perf/benchmark-subset.cc
@@ -97,7 +97,12 @@
{
auto *glyphs = hb_subset_input_glyph_set (input);
for (unsigned i = 0; i < subset_size && i < num_glyphs_in_font; i++) {
- // TODO(garretrieger): pick randomly.
+ if (i + 1 == subset_size &&
+ hb_subset_input_get_flags (input) & HB_SUBSET_FLAGS_RETAIN_GIDS)
+ {
+ hb_set_add (glyphs, num_glyphs_in_font - 1);
+ continue;
+ }
hb_set_add (glyphs, i);
}
}
@@ -111,17 +116,26 @@
return new_face;
}
+static hb_face_t *cached_face;
+
+static void
+free_cached_face (void)
+{
+ hb_face_destroy (cached_face);
+ cached_face = nullptr;
+}
+
+
/* benchmark for subsetting a font */
static void BM_subset (benchmark::State &state,
operation_t operation,
const test_input_t &test_input,
- bool hinting)
+ bool retain_gids)
{
unsigned subset_size = state.range(0);
hb_face_t *face = nullptr;
- static hb_face_t *cached_face;
static const char *cached_font_path;
if (!cached_font_path || strcmp (cached_font_path, test_input.font_path))
@@ -145,8 +159,8 @@
hb_subset_input_t* input = hb_subset_input_create_or_fail ();
assert (input);
- if (!hinting)
- hb_subset_input_set_flags (input, HB_SUBSET_FLAGS_NO_HINTING);
+ if (retain_gids)
+ hb_subset_input_set_flags (input, HB_SUBSET_FLAGS_RETAIN_GIDS);
switch (operation)
{
@@ -194,7 +208,7 @@
static void test_subset (operation_t op,
const char *op_name,
- bool hinting,
+ bool retain_gids,
benchmark::TimeUnit time_unit,
const test_input_t &test_input)
{
@@ -206,10 +220,10 @@
strcat (name, "/");
const char *p = strrchr (test_input.font_path, '/');
strcat (name, p ? p + 1 : test_input.font_path);
- if (!hinting)
- strcat (name, "/nohinting");
+ if (retain_gids)
+ strcat (name, "/retaingids");
- benchmark::RegisterBenchmark (name, BM_subset, op, test_input, hinting)
+ benchmark::RegisterBenchmark (name, BM_subset, op, test_input, retain_gids)
->Range(10, test_input.max_subset_size)
->Unit(time_unit);
}
@@ -232,6 +246,10 @@
{
benchmark::Initialize(&argc, argv);
+#ifndef HB_NO_ATEXIT
+ atexit (free_cached_face);
+#endif
+
if (argc > 1)
{
num_tests = (argc - 1) / 2;
@@ -245,9 +263,9 @@
#define TEST_OPERATION(op, time_unit) test_operation (op, #op, tests, num_tests, time_unit)
- TEST_OPERATION (subset_glyphs, benchmark::kMillisecond);
- TEST_OPERATION (subset_codepoints, benchmark::kMillisecond);
- TEST_OPERATION (instance, benchmark::kMillisecond);
+ TEST_OPERATION (subset_glyphs, benchmark::kMicrosecond);
+ TEST_OPERATION (subset_codepoints, benchmark::kMicrosecond);
+ TEST_OPERATION (instance, benchmark::kMicrosecond);
#undef TEST_OPERATION
diff --git a/src/Makefile.am b/src/Makefile.am
index 56dc32d..430d44a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -114,6 +114,16 @@
HB_HAS_CORETEXT_DEF = undef HB_HAS_CORETEXT
endif
+if HAVE_WASM
+HBCFLAGS += $(WASM_CFLAGS)
+HBNONPCLIBS += $(WASM_LIBS)
+HBSOURCES += $(HB_WASM_sources)
+HBHEADERS += $(HB_WASM_headers)
+HB_HAS_WASM_DEF = define HB_HAS_WASM 1
+else
+HB_HAS_WASM_DEF = undef HB_HAS_WASM
+endif
+
BUILT_SOURCES += \
hb-version.h
@@ -273,16 +283,17 @@
hb-features.h: hb-features.h.in $(top_builddir)/config.status
$(AM_V_GEN) $(SED) \
-e 's/mesondefine HB_HAS_CAIRO/$(HB_HAS_CAIRO_DEF)/' \
+ -e 's/mesondefine HB_HAS_CORETEXT/$(HB_HAS_CORETEXT_DEF)/' \
+ -e 's/mesondefine HB_HAS_DIRECTWRITE/$(HB_HAS_DIRECTWRITE_DEF)/' \
-e 's/mesondefine HB_HAS_FREETYPE/$(HB_HAS_FREETYPE_DEF)/' \
-e 's/mesondefine HB_HAS_GDI/$(HB_HAS_GDI_DEF)/' \
-e 's/mesondefine HB_HAS_GDI/$(HB_HAS_GDI_DEF)/' \
- -e 's/mesondefine HB_HAS_GRAPHITE/$(HB_HAS_GRAPHITE_DEF)/' \
-e 's/mesondefine HB_HAS_GLIB/$(HB_HAS_GLIB_DEF)/' \
-e 's/mesondefine HB_HAS_GOBJECT/$(HB_HAS_GOBJECT_DEF)/' \
- -e 's/mesondefine HB_HAS_UNISCRIBE/$(HB_HAS_UNISCRIBE_DEF)/' \
- -e 's/mesondefine HB_HAS_DIRECTWRITE/$(HB_HAS_DIRECTWRITE_DEF)/' \
- -e 's/mesondefine HB_HAS_CORETEXT/$(HB_HAS_CORETEXT_DEF)/' \
+ -e 's/mesondefine HB_HAS_GRAPHITE/$(HB_HAS_GRAPHITE_DEF)/' \
-e 's/mesondefine HB_HAS_ICU/$(HB_HAS_ICU_DEF)/' \
+ -e 's/mesondefine HB_HAS_UNISCRIBE/$(HB_HAS_UNISCRIBE_DEF)/' \
+ -e 's/mesondefine HB_HAS_WASM/$(HB_HAS_WASM_DEF)/' \
"$<" > "$@" || ($(RM) "$@"; false)
@@ -445,6 +456,8 @@
test-vector \
test-repacker \
test-classdef-graph \
+ test-instancer-solver \
+ test-tuple-varstore \
$(NULL)
COMPILED_TESTS_CPPFLAGS = $(HBCFLAGS) -DMAIN -UNDEBUG
COMPILED_TESTS_LDADD = libharfbuzz.la $(HBLIBS)
@@ -515,6 +528,14 @@
test_vector_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS)
test_vector_LDADD = $(COMPILED_TESTS_LDADD)
+test_instancer_solver_SOURCES = test-subset-instancer-solver.cc hb-subset-instancer-solver.cc hb-static.cc
+test_instancer_solver_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS)
+test_instancer_solver_LDADD = $(COMPILED_TESTS_LDADD)
+
+test_tuple_varstore_SOURCES = test-tuple-varstore.cc hb-subset-instancer-solver.cc hb-static.cc
+test_tuple_varstore_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS)
+test_tuple_varstore_LDADD = $(COMPILED_TESTS_LDADD)
+
dist_check_SCRIPTS = \
check-c-linkage-decls.py \
check-externs.py \
diff --git a/src/Makefile.sources b/src/Makefile.sources
index 85537a5..fbbff53 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -338,10 +338,22 @@
HB_UNISCRIBE_sources = hb-uniscribe.cc
HB_UNISCRIBE_headers = hb-uniscribe.h
-# Sources for libharfbuzz-gobject and libharfbuzz-icu
HB_ICU_sources = hb-icu.cc
HB_ICU_headers = hb-icu.h
+HB_WASM_sources = \
+ hb-wasm-api.cc \
+ hb-wasm-api.hh \
+ hb-wasm-api-blob.hh \
+ hb-wasm-api-buffer.hh \
+ hb-wasm-api-common.hh \
+ hb-wasm-api-face.hh \
+ hb-wasm-api-font.hh \
+ hb-wasm-api-shape.hh \
+ hb-wasm-shape.cc \
+ $(NULL)
+HB_WASM_headers = hb-wasm-api.h
+
# Sources for libharfbuzz-subset
HB_SUBSET_sources = \
hb-number.cc \
@@ -353,11 +365,10 @@
hb-subset-cff-common.cc \
hb-subset-cff-common.hh \
hb-subset-cff1.cc \
- hb-subset-cff1.hh \
hb-subset-cff2.cc \
- hb-subset-cff2.hh \
hb-subset-input.cc \
hb-subset-input.hh \
+ hb-subset-instancer-solver.hh \
hb-subset-instancer-solver.cc \
hb-subset-accelerator.hh \
hb-subset-plan.cc \
diff --git a/src/OT/Color/CBDT/CBDT.hh b/src/OT/Color/CBDT/CBDT.hh
index b125052..457039b 100644
--- a/src/OT/Color/CBDT/CBDT.hh
+++ b/src/OT/Color/CBDT/CBDT.hh
@@ -397,7 +397,6 @@
TRACE_SERIALIZE (this);
auto *subtable = c->serializer->start_embed<IndexSubtable> ();
- if (unlikely (!subtable)) return_trace (false);
if (unlikely (!c->serializer->extend_min (subtable))) return_trace (false);
auto *old_subtable = get_subtable (base);
@@ -545,7 +544,8 @@
const IndexSubtableRecord*>> *lookup /* OUT */) const
{
bool start_glyph_is_set = false;
- for (hb_codepoint_t new_gid = 0; new_gid < c->plan->num_output_glyphs (); new_gid++)
+ unsigned num_glyphs = c->plan->num_output_glyphs ();
+ for (hb_codepoint_t new_gid = 0; new_gid < num_glyphs; new_gid++)
{
hb_codepoint_t old_gid;
if (unlikely (!c->plan->old_gid_for_new_gid (new_gid, &old_gid))) continue;
@@ -576,9 +576,6 @@
{
TRACE_SUBSET (this);
- auto *dst = c->serializer->start_embed<IndexSubtableArray> ();
- if (unlikely (!dst)) return_trace (false);
-
hb_vector_t<hb_pair_t<hb_codepoint_t, const IndexSubtableRecord*>> lookup;
build_lookup (c, bitmap_size_context, &lookup);
if (unlikely (!c->serializer->propagate_error (lookup)))
@@ -993,12 +990,10 @@
{
TRACE_SUBSET (this);
- auto *cblc_prime = c->serializer->start_embed<CBLC> ();
-
// Use a vector as a secondary buffer as the tables need to be built in parallel.
hb_vector_t<char> cbdt_prime;
- if (unlikely (!cblc_prime)) return_trace (false);
+ auto *cblc_prime = c->serializer->start_embed<CBLC> ();
if (unlikely (!c->serializer->extend_min (cblc_prime))) return_trace (false);
cblc_prime->version = version;
diff --git a/src/OT/Color/COLR/COLR.hh b/src/OT/Color/COLR/COLR.hh
index 2a47984..6591bb4 100644
--- a/src/OT/Color/COLR/COLR.hh
+++ b/src/OT/Color/COLR/COLR.hh
@@ -409,7 +409,6 @@
{
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (this);
- if (unlikely (!out)) return_trace (false);
if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
if (!c->serializer->check_assign (out->extend, extend, HB_SERIALIZE_ERROR_INT_OVERFLOW)) return_trace (false);
@@ -1434,6 +1433,7 @@
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this) &&
+ c->check_ops (this->min_size) && // PainComposite can get exponential
src.sanitize (c, this) &&
backdrop.sanitize (c, this));
}
@@ -2167,7 +2167,7 @@
if (version == 0 && (!base_it || !layer_it))
return_trace (false);
- COLR *colr_prime = c->serializer->start_embed<COLR> ();
+ auto *colr_prime = c->serializer->start_embed<COLR> ();
if (unlikely (!c->serializer->extend_min (colr_prime))) return_trace (false);
if (version == 0)
diff --git a/src/OT/Color/sbix/sbix.hh b/src/OT/Color/sbix/sbix.hh
index 46ad3fd..ce8693c 100644
--- a/src/OT/Color/sbix/sbix.hh
+++ b/src/OT/Color/sbix/sbix.hh
@@ -48,7 +48,6 @@
{
TRACE_SERIALIZE (this);
SBIXGlyph* new_glyph = c->start_embed<SBIXGlyph> ();
- if (unlikely (!new_glyph)) return_trace (nullptr);
if (unlikely (!c->extend_min (new_glyph))) return_trace (nullptr);
new_glyph->xOffset = xOffset;
@@ -143,7 +142,6 @@
unsigned int num_output_glyphs = c->plan->num_output_glyphs ();
auto* out = c->serializer->start_embed<SBIXStrike> ();
- if (unlikely (!out)) return_trace (false);
auto snap = c->serializer->snapshot ();
if (unlikely (!c->serializer->extend (out, num_output_glyphs + 1))) return_trace (false);
out->ppem = ppem;
@@ -388,7 +386,6 @@
TRACE_SERIALIZE (this);
auto *out = c->serializer->start_embed<Array32OfOffset32To<SBIXStrike>> ();
- if (unlikely (!out)) return_trace (false);
if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
hb_vector_t<Offset32To<SBIXStrike>*> new_strikes;
@@ -423,8 +420,6 @@
{
TRACE_SUBSET (this);
- sbix *sbix_prime = c->serializer->start_embed<sbix> ();
- if (unlikely (!sbix_prime)) return_trace (false);
if (unlikely (!c->serializer->embed (this->version))) return_trace (false);
if (unlikely (!c->serializer->embed (this->flags))) return_trace (false);
diff --git a/src/OT/Layout/Common/Coverage.hh b/src/OT/Layout/Common/Coverage.hh
index 9ca88f7..25056c9 100644
--- a/src/OT/Layout/Common/Coverage.hh
+++ b/src/OT/Layout/Common/Coverage.hh
@@ -57,6 +57,9 @@
public:
DEFINE_SIZE_UNION (2, format);
+#ifndef HB_OPTIMIZE_SIZE
+ HB_ALWAYS_INLINE
+#endif
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
diff --git a/src/OT/Layout/Common/CoverageFormat1.hh b/src/OT/Layout/Common/CoverageFormat1.hh
index 5d68e3d..3f598d4 100644
--- a/src/OT/Layout/Common/CoverageFormat1.hh
+++ b/src/OT/Layout/Common/CoverageFormat1.hh
@@ -79,7 +79,7 @@
{
if (glyphArray.len > glyphs->get_population () * hb_bit_storage ((unsigned) glyphArray.len) / 2)
{
- for (hb_codepoint_t g = HB_SET_VALUE_INVALID; glyphs->next (&g);)
+ for (auto g : *glyphs)
if (get_coverage (g) != NOT_COVERED)
return true;
return false;
diff --git a/src/OT/Layout/Common/CoverageFormat2.hh b/src/OT/Layout/Common/CoverageFormat2.hh
index fa501d6..9c87542 100644
--- a/src/OT/Layout/Common/CoverageFormat2.hh
+++ b/src/OT/Layout/Common/CoverageFormat2.hh
@@ -122,7 +122,7 @@
{
if (rangeRecord.len > glyphs->get_population () * hb_bit_storage ((unsigned) rangeRecord.len) / 2)
{
- for (hb_codepoint_t g = HB_SET_VALUE_INVALID; glyphs->next (&g);)
+ for (auto g : *glyphs)
if (get_coverage (g) != NOT_COVERED)
return true;
return false;
diff --git a/src/OT/Layout/GDEF/GDEF.hh b/src/OT/Layout/GDEF/GDEF.hh
index c1ff796..d995ba0 100644
--- a/src/OT/Layout/GDEF/GDEF.hh
+++ b/src/OT/Layout/GDEF/GDEF.hh
@@ -49,8 +49,6 @@
{
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (*this);
- if (unlikely (!out)) return_trace (false);
-
return_trace (out->serialize (c->serializer, + iter ()));
}
};
@@ -202,7 +200,6 @@
{
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (*this);
- if (unlikely (!out)) return_trace (false);
if (!c->serializer->embed (caretValueFormat)) return_trace (false);
if (!c->serializer->embed (coordinate)) return_trace (false);
diff --git a/src/OT/Layout/GPOS/AnchorFormat3.hh b/src/OT/Layout/GPOS/AnchorFormat3.hh
index e7e3c5c..8684f60 100644
--- a/src/OT/Layout/GPOS/AnchorFormat3.hh
+++ b/src/OT/Layout/GPOS/AnchorFormat3.hh
@@ -25,7 +25,9 @@
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
- return_trace (c->check_struct (this) && xDeviceTable.sanitize (c, this) && yDeviceTable.sanitize (c, this));
+ if (unlikely (!c->check_struct (this))) return_trace (false);
+
+ return_trace (xDeviceTable.sanitize (c, this) && yDeviceTable.sanitize (c, this));
}
void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id HB_UNUSED,
@@ -35,9 +37,9 @@
*x = font->em_fscale_x (xCoordinate);
*y = font->em_fscale_y (yCoordinate);
- if (font->x_ppem || font->num_coords)
+ if ((font->x_ppem || font->num_coords) && xDeviceTable.sanitize (&c->sanitizer, this))
*x += (this+xDeviceTable).get_x_delta (font, c->var_store, c->var_store_cache);
- if (font->y_ppem || font->num_coords)
+ if ((font->y_ppem || font->num_coords) && yDeviceTable.sanitize (&c->sanitizer, this))
*y += (this+yDeviceTable).get_y_delta (font, c->var_store, c->var_store_cache);
}
@@ -45,7 +47,6 @@
{
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (*this);
- if (unlikely (!out)) return_trace (false);
if (unlikely (!c->serializer->embed (format))) return_trace (false);
if (unlikely (!c->serializer->embed (xCoordinate))) return_trace (false);
if (unlikely (!c->serializer->embed (yCoordinate))) return_trace (false);
diff --git a/src/OT/Layout/GPOS/AnchorMatrix.hh b/src/OT/Layout/GPOS/AnchorMatrix.hh
index c442efa..bd9b189 100644
--- a/src/OT/Layout/GPOS/AnchorMatrix.hh
+++ b/src/OT/Layout/GPOS/AnchorMatrix.hh
@@ -21,18 +21,25 @@
if (unlikely (hb_unsigned_mul_overflows (rows, cols))) return_trace (false);
unsigned int count = rows * cols;
if (!c->check_array (matrixZ.arrayZ, count)) return_trace (false);
+
+ if (c->lazy_some_gpos)
+ return_trace (true);
+
for (unsigned int i = 0; i < count; i++)
if (!matrixZ[i].sanitize (c, this)) return_trace (false);
return_trace (true);
}
- const Anchor& get_anchor (unsigned int row, unsigned int col,
- unsigned int cols, bool *found) const
+ const Anchor& get_anchor (hb_ot_apply_context_t *c,
+ unsigned int row, unsigned int col,
+ unsigned int cols, bool *found) const
{
*found = false;
if (unlikely (row >= rows || col >= cols)) return Null (Anchor);
- *found = !matrixZ[row * cols + col].is_null ();
- return this+matrixZ[row * cols + col];
+ auto &offset = matrixZ[row * cols + col];
+ if (unlikely (!offset.sanitize (&c->sanitizer, this))) return Null (Anchor);
+ *found = !offset.is_null ();
+ return this+offset;
}
template <typename Iterator,
diff --git a/src/OT/Layout/GPOS/CursivePosFormat1.hh b/src/OT/Layout/GPOS/CursivePosFormat1.hh
index b8773ba..a459124 100644
--- a/src/OT/Layout/GPOS/CursivePosFormat1.hh
+++ b/src/OT/Layout/GPOS/CursivePosFormat1.hh
@@ -278,7 +278,6 @@
const hb_map_t &glyph_map = *c->plan->glyph_map;
auto *out = c->serializer->start_embed (*this);
- if (unlikely (!out)) return_trace (false);
auto it =
+ hb_zip (this+coverage, entryExitRecord)
diff --git a/src/OT/Layout/GPOS/MarkArray.hh b/src/OT/Layout/GPOS/MarkArray.hh
index ff43ffb..34e2ab8 100644
--- a/src/OT/Layout/GPOS/MarkArray.hh
+++ b/src/OT/Layout/GPOS/MarkArray.hh
@@ -28,7 +28,7 @@
const Anchor& mark_anchor = this + record.markAnchor;
bool found;
- const Anchor& glyph_anchor = anchors.get_anchor (glyph_index, mark_class, class_count, &found);
+ const Anchor& glyph_anchor = anchors.get_anchor (c, glyph_index, mark_class, class_count, &found);
/* If this subtable doesn't have an anchor for this base and this class,
* return false such that the subsequent subtables have a chance at it. */
if (unlikely (!found)) return_trace (false);
diff --git a/src/OT/Layout/GPOS/PairPosFormat2.hh b/src/OT/Layout/GPOS/PairPosFormat2.hh
index 31329df..05e6c0f 100644
--- a/src/OT/Layout/GPOS/PairPosFormat2.hh
+++ b/src/OT/Layout/GPOS/PairPosFormat2.hh
@@ -54,8 +54,9 @@
return_trace (c->check_range ((const void *) values,
count,
stride) &&
- valueFormat1.sanitize_values_stride_unsafe (c, this, &values[0], count, stride) &&
- valueFormat2.sanitize_values_stride_unsafe (c, this, &values[len1], count, stride));
+ (c->lazy_some_gpos ||
+ (valueFormat1.sanitize_values_stride_unsafe (c, this, &values[0], count, stride) &&
+ valueFormat2.sanitize_values_stride_unsafe (c, this, &values[len1], count, stride))));
}
bool intersects (const hb_set_t *glyphs) const
@@ -298,11 +299,13 @@
out->valueFormat2 = out->valueFormat2.drop_device_table_flags ();
}
+ unsigned total_len = len1 + len2;
+ hb_vector_t<unsigned> class2_idxs (+ hb_range ((unsigned) class2Count) | hb_filter (klass2_map));
for (unsigned class1_idx : + hb_range ((unsigned) class1Count) | hb_filter (klass1_map))
{
- for (unsigned class2_idx : + hb_range ((unsigned) class2Count) | hb_filter (klass2_map))
+ for (unsigned class2_idx : class2_idxs)
{
- unsigned idx = (class1_idx * (unsigned) class2Count + class2_idx) * (len1 + len2);
+ unsigned idx = (class1_idx * (unsigned) class2Count + class2_idx) * total_len;
valueFormat1.copy_values (c->serializer, out->valueFormat1, this, &values[idx], &c->plan->layout_variation_idx_delta_map);
valueFormat2.copy_values (c->serializer, out->valueFormat2, this, &values[idx + len1], &c->plan->layout_variation_idx_delta_map);
}
diff --git a/src/OT/Layout/GPOS/PairSet.hh b/src/OT/Layout/GPOS/PairSet.hh
index 9faff49..db301bb 100644
--- a/src/OT/Layout/GPOS/PairSet.hh
+++ b/src/OT/Layout/GPOS/PairSet.hh
@@ -52,8 +52,9 @@
unsigned int count = len;
const PairValueRecord *record = &firstPairValueRecord;
- return_trace (closure->valueFormats[0].sanitize_values_stride_unsafe (c, this, &record->values[0], count, closure->stride) &&
- closure->valueFormats[1].sanitize_values_stride_unsafe (c, this, &record->values[closure->len1], count, closure->stride));
+ return_trace (c->lazy_some_gpos ||
+ (closure->valueFormats[0].sanitize_values_stride_unsafe (c, this, &record->values[0], count, closure->stride) &&
+ closure->valueFormats[1].sanitize_values_stride_unsafe (c, this, &record->values[closure->len1], count, closure->stride)));
}
bool intersects (const hb_set_t *glyphs,
diff --git a/src/OT/Layout/GPOS/PairValueRecord.hh b/src/OT/Layout/GPOS/PairValueRecord.hh
index 3222477..c99a386 100644
--- a/src/OT/Layout/GPOS/PairValueRecord.hh
+++ b/src/OT/Layout/GPOS/PairValueRecord.hh
@@ -22,7 +22,8 @@
ValueRecord values; /* Positioning data for the first glyph
* followed by for second glyph */
public:
- DEFINE_SIZE_ARRAY (Types::size, values);
+ DEFINE_SIZE_ARRAY (Types::HBGlyphID::static_size, values);
+ DEFINE_SIZE_MAX (Types::HBGlyphID::static_size + 2 * Value::static_size * 8 * sizeof (ValueFormat));
int cmp (hb_codepoint_t k) const
{ return secondGlyph.cmp (k); }
diff --git a/src/OT/Layout/GPOS/SinglePosFormat1.hh b/src/OT/Layout/GPOS/SinglePosFormat1.hh
index 623e4e6..dff1f73 100644
--- a/src/OT/Layout/GPOS/SinglePosFormat1.hh
+++ b/src/OT/Layout/GPOS/SinglePosFormat1.hh
@@ -90,6 +90,7 @@
bool
position_single (hb_font_t *font,
+ hb_blob_t *table_blob,
hb_direction_t direction,
hb_codepoint_t gid,
hb_glyph_position_t &pos) const
@@ -100,7 +101,7 @@
/* This is ugly... */
hb_buffer_t buffer;
buffer.props.direction = direction;
- OT::hb_ot_apply_context_t c (1, font, &buffer);
+ OT::hb_ot_apply_context_t c (1, font, &buffer, table_blob);
valueFormat.apply_value (&c, this, values, pos);
return true;
diff --git a/src/OT/Layout/GPOS/SinglePosFormat2.hh b/src/OT/Layout/GPOS/SinglePosFormat2.hh
index e8f2d7c..168ad3b 100644
--- a/src/OT/Layout/GPOS/SinglePosFormat2.hh
+++ b/src/OT/Layout/GPOS/SinglePosFormat2.hh
@@ -94,6 +94,7 @@
bool
position_single (hb_font_t *font,
+ hb_blob_t *table_blob,
hb_direction_t direction,
hb_codepoint_t gid,
hb_glyph_position_t &pos) const
@@ -105,7 +106,7 @@
/* This is ugly... */
hb_buffer_t buffer;
buffer.props.direction = direction;
- OT::hb_ot_apply_context_t c (1, font, &buffer);
+ OT::hb_ot_apply_context_t c (1, font, &buffer, table_blob);
valueFormat.apply_value (&c, this,
&values[index * valueFormat.get_len ()],
diff --git a/src/OT/Layout/GPOS/ValueFormat.hh b/src/OT/Layout/GPOS/ValueFormat.hh
index 1aa451a..461a13d 100644
--- a/src/OT/Layout/GPOS/ValueFormat.hh
+++ b/src/OT/Layout/GPOS/ValueFormat.hh
@@ -118,21 +118,25 @@
auto *cache = c->var_store_cache;
/* pixel -> fractional pixel */
- if (format & xPlaDevice) {
- if (use_x_device) glyph_pos.x_offset += (base + get_device (values, &ret)).get_x_delta (font, store, cache);
+ if (format & xPlaDevice)
+ {
+ if (use_x_device) glyph_pos.x_offset += get_device (values, &ret, base, c->sanitizer).get_x_delta (font, store, cache);
values++;
}
- if (format & yPlaDevice) {
- if (use_y_device) glyph_pos.y_offset += (base + get_device (values, &ret)).get_y_delta (font, store, cache);
+ if (format & yPlaDevice)
+ {
+ if (use_y_device) glyph_pos.y_offset += get_device (values, &ret, base, c->sanitizer).get_y_delta (font, store, cache);
values++;
}
- if (format & xAdvDevice) {
- if (horizontal && use_x_device) glyph_pos.x_advance += (base + get_device (values, &ret)).get_x_delta (font, store, cache);
+ if (format & xAdvDevice)
+ {
+ if (horizontal && use_x_device) glyph_pos.x_advance += get_device (values, &ret, base, c->sanitizer).get_x_delta (font, store, cache);
values++;
}
- if (format & yAdvDevice) {
+ if (format & yAdvDevice)
+ {
/* y_advance values grow downward but font-space grows upward, hence negation */
- if (!horizontal && use_y_device) glyph_pos.y_advance -= (base + get_device (values, &ret)).get_y_delta (font, store, cache);
+ if (!horizontal && use_y_device) glyph_pos.y_advance -= get_device (values, &ret, base, c->sanitizer).get_y_delta (font, store, cache);
values++;
}
return ret;
@@ -174,6 +178,9 @@
if (format & xAdvance) x_adv = copy_value (c, new_format, xAdvance, *values++);
if (format & yAdvance) y_adv = copy_value (c, new_format, yAdvance, *values++);
+ if (!has_device ())
+ return;
+
if (format & xPlaDevice)
{
add_delta_to_value (x_placement, base, values, layout_variation_idx_delta_map);
@@ -233,14 +240,12 @@
if (format & ValueFormat::xAdvDevice)
{
-
(base + get_device (&(values[i]))).collect_variation_indices (c);
i++;
}
if (format & ValueFormat::yAdvDevice)
{
-
(base + get_device (&(values[i]))).collect_variation_indices (c);
i++;
}
@@ -277,10 +282,22 @@
{
return *static_cast<Offset16To<Device> *> (value);
}
- static inline const Offset16To<Device>& get_device (const Value* value, bool *worked=nullptr)
+ static inline const Offset16To<Device>& get_device (const Value* value)
+ {
+ return *static_cast<const Offset16To<Device> *> (value);
+ }
+ static inline const Device& get_device (const Value* value,
+ bool *worked,
+ const void *base,
+ hb_sanitize_context_t &c)
{
if (worked) *worked |= bool (*value);
- return *static_cast<const Offset16To<Device> *> (value);
+ auto &offset = *static_cast<const Offset16To<Device> *> (value);
+
+ if (unlikely (!offset.sanitize (&c, base)))
+ return Null(Device);
+
+ return base + offset;
}
void add_delta_to_value (HBINT16 *value,
@@ -340,25 +357,26 @@
bool sanitize_value (hb_sanitize_context_t *c, const void *base, const Value *values) const
{
TRACE_SANITIZE (this);
- return_trace (c->check_range (values, get_size ()) && (!has_device () || sanitize_value_devices (c, base, values)));
+
+ if (unlikely (!c->check_range (values, get_size ()))) return_trace (false);
+
+ if (c->lazy_some_gpos)
+ return_trace (true);
+
+ return_trace (!has_device () || sanitize_value_devices (c, base, values));
}
bool sanitize_values (hb_sanitize_context_t *c, const void *base, const Value *values, unsigned int count) const
{
TRACE_SANITIZE (this);
- unsigned int len = get_len ();
+ unsigned size = get_size ();
- if (!c->check_range (values, count, get_size ())) return_trace (false);
+ if (!c->check_range (values, count, size)) return_trace (false);
- if (!has_device ()) return_trace (true);
+ if (c->lazy_some_gpos)
+ return_trace (true);
- for (unsigned int i = 0; i < count; i++) {
- if (!sanitize_value_devices (c, base, values))
- return_trace (false);
- values += len;
- }
-
- return_trace (true);
+ return_trace (sanitize_values_stride_unsafe (c, base, values, count, size));
}
/* Just sanitize referenced Device tables. Doesn't check the values themselves. */
diff --git a/src/OT/Layout/GSUB/Common.hh b/src/OT/Layout/GSUB/Common.hh
index 968bba0..b849494 100644
--- a/src/OT/Layout/GSUB/Common.hh
+++ b/src/OT/Layout/GSUB/Common.hh
@@ -8,8 +8,6 @@
namespace Layout {
namespace GSUB_impl {
-typedef hb_pair_t<hb_codepoint_t, hb_codepoint_t> hb_codepoint_pair_t;
-
template<typename Iterator>
static void SingleSubst_serialize (hb_serialize_context_t *c,
Iterator it);
diff --git a/src/OT/Layout/GSUB/Ligature.hh b/src/OT/Layout/GSUB/Ligature.hh
index 8674a52..db3fc55 100644
--- a/src/OT/Layout/GSUB/Ligature.hh
+++ b/src/OT/Layout/GSUB/Ligature.hh
@@ -13,12 +13,13 @@
public:
typename Types::HBGlyphID
ligGlyph; /* GlyphID of ligature to substitute */
- HeadlessArrayOf<typename Types::HBGlyphID>
+ HeadlessArray16Of<typename Types::HBGlyphID>
component; /* Array of component GlyphIDs--start
* with the second component--ordered
* in writing direction */
public:
DEFINE_SIZE_ARRAY (Types::size + 2, component);
+ DEFINE_SIZE_MAX (65536 * Types::HBGlyphID::static_size);
bool sanitize (hb_sanitize_context_t *c) const
{
diff --git a/src/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh b/src/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh
index 2c2e1aa..916fa28 100644
--- a/src/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh
+++ b/src/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh
@@ -191,7 +191,6 @@
TRACE_SERIALIZE (this);
auto *out = c->serializer->start_embed (this);
- if (unlikely (!c->serializer->check_success (out))) return_trace (false);
if (unlikely (!c->serializer->embed (this->format))) return_trace (false);
if (unlikely (!c->serializer->embed (this->coverage))) return_trace (false);
diff --git a/src/OT/Layout/GSUB/Sequence.hh b/src/OT/Layout/GSUB/Sequence.hh
index ae3292f..a26cf8c 100644
--- a/src/OT/Layout/GSUB/Sequence.hh
+++ b/src/OT/Layout/GSUB/Sequence.hh
@@ -53,7 +53,7 @@
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->message (c->font,
- "replaced glyph at %u (multiple subtitution)",
+ "replaced glyph at %u (multiple substitution)",
c->buffer->idx - 1u);
}
diff --git a/src/OT/glyf/CompositeGlyph.hh b/src/OT/glyf/CompositeGlyph.hh
index d81fadf..60858a5 100644
--- a/src/OT/glyf/CompositeGlyph.hh
+++ b/src/OT/glyf/CompositeGlyph.hh
@@ -90,24 +90,36 @@
static void transform (const float (&matrix)[4],
hb_array_t<contour_point_t> points)
{
- auto arrayZ = points.arrayZ;
- unsigned count = points.length;
-
if (matrix[0] != 1.f || matrix[1] != 0.f ||
matrix[2] != 0.f || matrix[3] != 1.f)
- for (unsigned i = 0; i < count; i++)
- arrayZ[i].transform (matrix);
+ for (auto &point : points)
+ point.transform (matrix);
}
static void translate (const contour_point_t &trans,
hb_array_t<contour_point_t> points)
{
- auto arrayZ = points.arrayZ;
- unsigned count = points.length;
-
- if (trans.x != 0.f || trans.y != 0.f)
- for (unsigned i = 0; i < count; i++)
- arrayZ[i].translate (trans);
+ if (HB_OPTIMIZE_SIZE_VAL)
+ {
+ if (trans.x != 0.f || trans.y != 0.f)
+ for (auto &point : points)
+ point.translate (trans);
+ }
+ else
+ {
+ if (trans.x != 0.f && trans.y != 0.f)
+ for (auto &point : points)
+ point.translate (trans);
+ else
+ {
+ if (trans.x != 0.f)
+ for (auto &point : points)
+ point.x += trans.x;
+ else if (trans.y != 0.f)
+ for (auto &point : points)
+ point.y += trans.y;
+ }
+ }
}
void transform_points (hb_array_t<contour_point_t> points,
@@ -131,9 +143,8 @@
float matrix[4];
contour_point_t trans;
get_transformation (matrix, trans);
- points.alloc (points.length + 4); // For phantom points
- if (unlikely (!points.resize (points.length + 1))) return false;
- points.arrayZ[points.length - 1] = trans;
+ if (unlikely (!points.alloc (points.length + 4))) return false; // For phantom points
+ points.push (trans);
return true;
}
@@ -382,7 +393,7 @@
{
/* last 4 points in points_with_deltas are phantom points and should not be included */
if (i >= points_with_deltas.length - 4) {
- free (o);
+ hb_free (o);
return false;
}
diff --git a/src/OT/glyf/Glyph.hh b/src/OT/glyf/Glyph.hh
index 2bd5fe8..2611c1e 100644
--- a/src/OT/glyf/Glyph.hh
+++ b/src/OT/glyf/Glyph.hh
@@ -114,8 +114,8 @@
if (type != EMPTY)
{
- plan->bounds_width_map.set (new_gid, xMax - xMin);
- plan->bounds_height_map.set (new_gid, yMax - yMin);
+ plan->bounds_width_vec[new_gid] = xMax - xMin;
+ plan->bounds_height_vec[new_gid] = yMax - yMin;
}
unsigned len = all_points.length;
@@ -124,10 +124,12 @@
float topSideY = all_points[len - 2].y;
float bottomSideY = all_points[len - 1].y;
+ uint32_t hash = hb_hash (new_gid);
+
signed hori_aw = roundf (rightSideX - leftSideX);
if (hori_aw < 0) hori_aw = 0;
int lsb = roundf (xMin - leftSideX);
- plan->hmtx_map.set (new_gid, hb_pair ((unsigned) hori_aw, lsb));
+ plan->hmtx_map.set_with_hash (new_gid, hash, hb_pair ((unsigned) hori_aw, lsb));
//flag value should be computed using non-empty glyphs
if (type != EMPTY && lsb != xMin)
plan->head_maxp_info.allXMinIsLsb = false;
@@ -135,7 +137,7 @@
signed vert_aw = roundf (topSideY - bottomSideY);
if (vert_aw < 0) vert_aw = 0;
int tsb = roundf (topSideY - yMax);
- plan->vmtx_map.set (new_gid, hb_pair ((unsigned) vert_aw, tsb));
+ plan->vmtx_map.set_with_hash (new_gid, hash, hb_pair ((unsigned) vert_aw, tsb));
}
bool compile_header_bytes (const hb_subset_plan_t *plan,
@@ -369,9 +371,11 @@
}
#ifndef HB_NO_VAR
- glyf_accelerator.gvar->apply_deltas_to_points (gid,
- coords,
- points.as_array ().sub_array (old_length));
+ if (coords)
+ glyf_accelerator.gvar->apply_deltas_to_points (gid,
+ coords,
+ points.as_array ().sub_array (old_length),
+ phantom_only && type == SIMPLE);
#endif
// mainly used by CompositeGlyph calculating new X/Y offset value so no need to extend it
@@ -379,7 +383,7 @@
if (points_with_deltas != nullptr && depth == 0 && type == COMPOSITE)
{
if (unlikely (!points_with_deltas->resize (points.length))) return false;
- points_with_deltas->copy_vector (points);
+ *points_with_deltas = points;
}
switch (type) {
@@ -417,14 +421,17 @@
for (unsigned int i = 0; i < PHANTOM_COUNT; i++)
phantoms[i] = comp_points[comp_points.length - PHANTOM_COUNT + i];
- float matrix[4];
- contour_point_t default_trans;
- item.get_transformation (matrix, default_trans);
+ if (comp_points) // Empty in case of phantom_only
+ {
+ float matrix[4];
+ contour_point_t default_trans;
+ item.get_transformation (matrix, default_trans);
- /* Apply component transformation & translation (with deltas applied) */
- item.transform_points (comp_points, matrix, points[comp_index]);
+ /* Apply component transformation & translation (with deltas applied) */
+ item.transform_points (comp_points, matrix, points[comp_index]);
+ }
- if (item.is_anchored ())
+ if (item.is_anchored () && !phantom_only)
{
unsigned int p1, p2;
item.get_anchor_points (p1, p2);
@@ -466,7 +473,10 @@
assert (record_points.length == item_num_points);
auto component_coords = coords;
- if (item.is_reset_unspecified_axes ())
+ /* Copying coords is expensive; so we have put an arbitrary
+ * limit on the max number of coords for now. */
+ if (item.is_reset_unspecified_axes () ||
+ coords.length > HB_GLYF_VAR_COMPOSITE_MAX_AXES)
component_coords = hb_array<int> ();
coord_setter_t coord_setter (component_coords);
diff --git a/src/OT/glyf/SimpleGlyph.hh b/src/OT/glyf/SimpleGlyph.hh
index 555bcee..1d42cc2 100644
--- a/src/OT/glyf/SimpleGlyph.hh
+++ b/src/OT/glyf/SimpleGlyph.hh
@@ -154,10 +154,9 @@
{
int v = 0;
- unsigned count = points_.length;
- for (unsigned i = 0; i < count; i++)
+ for (auto &point : points_)
{
- unsigned flag = points_.arrayZ[i].flag;
+ unsigned flag = point.flag;
if (flag & short_flag)
{
if (unlikely (p + 1 > end)) return false;
@@ -175,7 +174,7 @@
p += HBINT16::static_size;
}
}
- points_.arrayZ[i].*m = v;
+ point.*m = v;
}
return true;
}
@@ -192,9 +191,10 @@
unsigned old_length = points.length;
points.alloc (points.length + num_points + 4, true); // Allocate for phantom points, to avoid a possible copy
- if (!points.resize (points.length + num_points, false)) return false;
+ if (unlikely (!points.resize (points.length + num_points, false))) return false;
auto points_ = points.as_array ().sub_array (old_length);
- hb_memset (points_.arrayZ, 0, sizeof (contour_point_t) * num_points);
+ if (!phantom_only)
+ hb_memset (points_.arrayZ, 0, sizeof (contour_point_t) * num_points);
if (phantom_only) return true;
for (int i = 0; i < num_contours; i++)
diff --git a/src/OT/glyf/SubsetGlyph.hh b/src/OT/glyf/SubsetGlyph.hh
index 26dc374..8099d3c 100644
--- a/src/OT/glyf/SubsetGlyph.hh
+++ b/src/OT/glyf/SubsetGlyph.hh
@@ -22,7 +22,7 @@
bool serialize (hb_serialize_context_t *c,
bool use_short_loca,
- const hb_subset_plan_t *plan)
+ const hb_subset_plan_t *plan) const
{
TRACE_SERIALIZE (this);
@@ -40,7 +40,7 @@
pad = 0;
while (pad_length > 0)
{
- c->embed (pad);
+ (void) c->embed (pad);
pad_length--;
}
diff --git a/src/OT/glyf/VarCompositeGlyph.hh b/src/OT/glyf/VarCompositeGlyph.hh
index 6dc6fd9..50cbece 100644
--- a/src/OT/glyf/VarCompositeGlyph.hh
+++ b/src/OT/glyf/VarCompositeGlyph.hh
@@ -214,7 +214,7 @@
points.alloc (points.length + num_points + 4); // For phantom points
if (unlikely (!points.resize (points.length + num_points, false))) return false;
contour_point_t *rec_points = points.arrayZ + (points.length - num_points);
- memset (rec_points, 0, num_points * sizeof (rec_points[0]));
+ hb_memset (rec_points, 0, num_points * sizeof (rec_points[0]));
unsigned fl = flags;
diff --git a/src/OT/glyf/coord-setter.hh b/src/OT/glyf/coord-setter.hh
index df64ed5..cf05929 100644
--- a/src/OT/glyf/coord-setter.hh
+++ b/src/OT/glyf/coord-setter.hh
@@ -16,6 +16,8 @@
int& operator [] (unsigned idx)
{
+ if (unlikely (idx >= HB_GLYF_VAR_COMPOSITE_MAX_AXES))
+ return Crap(int);
if (coords.length < idx + 1)
coords.resize (idx + 1);
return coords[idx];
diff --git a/src/OT/glyf/glyf-helpers.hh b/src/OT/glyf/glyf-helpers.hh
index 30106b2..d0a5a13 100644
--- a/src/OT/glyf/glyf-helpers.hh
+++ b/src/OT/glyf/glyf-helpers.hh
@@ -12,24 +12,44 @@
namespace glyf_impl {
-template<typename IteratorIn, typename IteratorOut,
- hb_requires (hb_is_source_of (IteratorIn, unsigned int)),
- hb_requires (hb_is_sink_of (IteratorOut, unsigned))>
+template<typename IteratorIn, typename TypeOut,
+ hb_requires (hb_is_source_of (IteratorIn, unsigned int))>
static void
-_write_loca (IteratorIn&& it, bool short_offsets, IteratorOut&& dest)
+_write_loca (IteratorIn&& it,
+ const hb_sorted_vector_t<hb_codepoint_pair_t> new_to_old_gid_list,
+ bool short_offsets,
+ TypeOut *dest,
+ unsigned num_offsets)
{
unsigned right_shift = short_offsets ? 1 : 0;
- unsigned int offset = 0;
- dest << 0;
- + it
- | hb_map ([=, &offset] (unsigned int padded_size)
- {
- offset += padded_size;
- DEBUG_MSG (SUBSET, nullptr, "loca entry offset %u", offset);
- return offset >> right_shift;
- })
- | hb_sink (dest)
- ;
+ unsigned offset = 0;
+ TypeOut value;
+ value = 0;
+ *dest++ = value;
+ hb_codepoint_t last = 0;
+ for (auto _ : new_to_old_gid_list)
+ {
+ hb_codepoint_t gid = _.first;
+ for (; last < gid; last++)
+ {
+ DEBUG_MSG (SUBSET, nullptr, "loca entry empty offset %u", offset);
+ *dest++ = value;
+ }
+
+ unsigned padded_size = *it++;
+ offset += padded_size;
+ DEBUG_MSG (SUBSET, nullptr, "loca entry gid %u offset %u padded-size %u", gid, offset, padded_size);
+ value = offset >> right_shift;
+ *dest++ = value;
+
+ last++; // Skip over gid
+ }
+ unsigned num_glyphs = num_offsets - 1;
+ for (; last < num_glyphs; last++)
+ {
+ DEBUG_MSG (SUBSET, nullptr, "loca entry empty offset %u", offset);
+ *dest++ = value;
+ }
}
static bool
@@ -67,11 +87,14 @@
template<typename Iterator,
hb_requires (hb_is_source_of (Iterator, unsigned int))>
static bool
-_add_loca_and_head (hb_subset_plan_t * plan, Iterator padded_offsets, bool use_short_loca)
+_add_loca_and_head (hb_subset_context_t *c,
+ Iterator padded_offsets,
+ bool use_short_loca)
{
- unsigned num_offsets = padded_offsets.len () + 1;
+ unsigned num_offsets = c->plan->num_output_glyphs () + 1;
unsigned entry_size = use_short_loca ? 2 : 4;
- char *loca_prime_data = (char *) hb_calloc (entry_size, num_offsets);
+
+ char *loca_prime_data = (char *) hb_malloc (entry_size * num_offsets);
if (unlikely (!loca_prime_data)) return false;
@@ -79,9 +102,9 @@
entry_size, num_offsets, entry_size * num_offsets);
if (use_short_loca)
- _write_loca (padded_offsets, true, hb_array ((HBUINT16 *) loca_prime_data, num_offsets));
+ _write_loca (padded_offsets, c->plan->new_to_old_gid_list, true, (HBUINT16 *) loca_prime_data, num_offsets);
else
- _write_loca (padded_offsets, false, hb_array ((HBUINT32 *) loca_prime_data, num_offsets));
+ _write_loca (padded_offsets, c->plan->new_to_old_gid_list, false, (HBUINT32 *) loca_prime_data, num_offsets);
hb_blob_t *loca_blob = hb_blob_create (loca_prime_data,
entry_size * num_offsets,
@@ -89,8 +112,8 @@
loca_prime_data,
hb_free);
- bool result = plan->add_table (HB_OT_TAG_loca, loca_blob)
- && _add_head_and_set_loca_version (plan, use_short_loca);
+ bool result = c->plan->add_table (HB_OT_TAG_loca, loca_blob)
+ && _add_head_and_set_loca_version (c->plan, use_short_loca);
hb_blob_destroy (loca_blob);
return result;
diff --git a/src/OT/glyf/glyf.hh b/src/OT/glyf/glyf.hh
index dd08dda..6300cf4 100644
--- a/src/OT/glyf/glyf.hh
+++ b/src/OT/glyf/glyf.hh
@@ -85,75 +85,72 @@
return_trace (false);
}
- glyf *glyf_prime = c->serializer->start_embed <glyf> ();
- if (unlikely (!c->serializer->check_success (glyf_prime))) return_trace (false);
-
hb_font_t *font = nullptr;
if (c->plan->normalized_coords)
{
font = _create_font_for_instancing (c->plan);
- if (unlikely (!font)) return false;
+ if (unlikely (!font))
+ return_trace (false);
}
hb_vector_t<unsigned> padded_offsets;
- unsigned num_glyphs = c->plan->num_output_glyphs ();
- if (unlikely (!padded_offsets.resize (num_glyphs)))
- {
- hb_font_destroy (font);
- return false;
- }
+ if (unlikely (!padded_offsets.alloc (c->plan->new_to_old_gid_list.length, true)))
+ return_trace (false);
hb_vector_t<glyf_impl::SubsetGlyph> glyphs;
if (!_populate_subset_glyphs (c->plan, font, glyphs))
{
hb_font_destroy (font);
- return false;
+ return_trace (false);
}
if (font)
hb_font_destroy (font);
unsigned max_offset = 0;
- for (unsigned i = 0; i < num_glyphs; i++)
+ for (auto &g : glyphs)
{
- padded_offsets[i] = glyphs[i].padded_size ();
- max_offset += padded_offsets[i];
+ unsigned size = g.padded_size ();
+ padded_offsets.push (size);
+ max_offset += size;
}
bool use_short_loca = false;
if (likely (!c->plan->force_long_loca))
use_short_loca = max_offset < 0x1FFFF;
- if (!use_short_loca) {
- for (unsigned i = 0; i < num_glyphs; i++)
- padded_offsets[i] = glyphs[i].length ();
+ if (!use_short_loca)
+ {
+ padded_offsets.resize (0);
+ for (auto &g : glyphs)
+ padded_offsets.push (g.length ());
}
- bool result = glyf_prime->serialize (c->serializer, glyphs.writer (), use_short_loca, c->plan);
+ auto *glyf_prime = c->serializer->start_embed <glyf> ();
+ bool result = glyf_prime->serialize (c->serializer, hb_iter (glyphs), use_short_loca, c->plan);
if (c->plan->normalized_coords && !c->plan->pinned_at_default)
_free_compiled_subset_glyphs (glyphs);
- if (!result) return false;
+ if (unlikely (!c->serializer->check_success (glyf_impl::_add_loca_and_head (c,
+ padded_offsets.iter (),
+ use_short_loca))))
+ return_trace (false);
- if (unlikely (c->serializer->in_error ())) return_trace (false);
-
- return_trace (c->serializer->check_success (glyf_impl::_add_loca_and_head (c->plan,
- padded_offsets.iter (),
- use_short_loca)));
+ return result;
}
bool
_populate_subset_glyphs (const hb_subset_plan_t *plan,
hb_font_t *font,
- hb_vector_t<glyf_impl::SubsetGlyph> &glyphs /* OUT */) const;
+ hb_vector_t<glyf_impl::SubsetGlyph>& glyphs /* OUT */) const;
hb_font_t *
_create_font_for_instancing (const hb_subset_plan_t *plan) const;
void _free_compiled_subset_glyphs (hb_vector_t<glyf_impl::SubsetGlyph> &glyphs) const
{
- for (unsigned i = 0; i < glyphs.length; i++)
- glyphs[i].free_compiled_bytes ();
+ for (auto &g : glyphs)
+ g.free_compiled_bytes ();
}
protected:
@@ -222,13 +219,14 @@
if (unlikely (!glyph_for_gid (gid).get_points (font, *this, all_points, nullptr, nullptr, nullptr, true, true, phantom_only)))
return false;
+ unsigned count = all_points.length;
+ assert (count >= glyf_impl::PHANTOM_COUNT);
+ count -= glyf_impl::PHANTOM_COUNT;
+
if (consumer.is_consuming_contour_points ())
{
- unsigned count = all_points.length;
- assert (count >= glyf_impl::PHANTOM_COUNT);
- count -= glyf_impl::PHANTOM_COUNT;
- for (unsigned point_index = 0; point_index < count; point_index++)
- consumer.consume_point (all_points[point_index]);
+ for (auto &point : all_points.as_array ().sub_array (0, count))
+ consumer.consume_point (point);
consumer.points_end ();
}
@@ -236,7 +234,7 @@
contour_point_t *phantoms = consumer.get_phantoms_sink ();
if (phantoms)
for (unsigned i = 0; i < glyf_impl::PHANTOM_COUNT; ++i)
- phantoms[i] = all_points[all_points.length - glyf_impl::PHANTOM_COUNT + i];
+ phantoms[i] = all_points.arrayZ[count + i];
return true;
}
@@ -299,6 +297,7 @@
if (extents) bounds = contour_bounds_t ();
}
+ HB_ALWAYS_INLINE
void consume_point (const contour_point_t &point) { bounds.add (point); }
void points_end () { bounds.get_extents (font, extents, scaled); }
@@ -431,16 +430,17 @@
hb_vector_t<glyf_impl::SubsetGlyph>& glyphs /* OUT */) const
{
OT::glyf_accelerator_t glyf (plan->source);
- unsigned num_glyphs = plan->num_output_glyphs ();
- if (!glyphs.resize (num_glyphs)) return false;
+ if (!glyphs.alloc (plan->new_to_old_gid_list.length, true)) return false;
- for (auto p : plan->glyph_map->iter ())
+ for (const auto &pair : plan->new_to_old_gid_list)
{
- unsigned new_gid = p.second;
- glyf_impl::SubsetGlyph& subset_glyph = glyphs.arrayZ[new_gid];
- subset_glyph.old_gid = p.first;
+ hb_codepoint_t new_gid = pair.first;
+ hb_codepoint_t old_gid = pair.second;
+ glyf_impl::SubsetGlyph *p = glyphs.push ();
+ glyf_impl::SubsetGlyph& subset_glyph = *p;
+ subset_glyph.old_gid = old_gid;
- if (unlikely (new_gid == 0 &&
+ if (unlikely (old_gid == 0 && new_gid == 0 &&
!(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE)) &&
!plan->normalized_coords)
subset_glyph.source_glyph = glyf_impl::Glyph ();
@@ -487,7 +487,7 @@
{
hb_variation_t var;
var.tag = _.first;
- var.value = _.second;
+ var.value = _.second.middle;
vars.push (var);
}
diff --git a/src/OT/glyf/path-builder.hh b/src/OT/glyf/path-builder.hh
index f7f732d..f550524 100644
--- a/src/OT/glyf/path-builder.hh
+++ b/src/OT/glyf/path-builder.hh
@@ -21,11 +21,11 @@
operator bool () const { return has_data; }
bool has_data = false;
- float x = 0.;
- float y = 0.;
+ float x;
+ float y;
- optional_point_t lerp (optional_point_t p, float t)
- { return optional_point_t (x + t * (p.x - x), y + t * (p.y - y)); }
+ optional_point_t mid (optional_point_t p)
+ { return optional_point_t ((x + p.x) * 0.5f, (y + p.y) * 0.5f); }
} first_oncurve, first_offcurve, first_offcurve2, last_offcurve, last_offcurve2;
path_builder_t (hb_font_t *font_, hb_draw_session_t &draw_session_) :
@@ -37,6 +37,7 @@
* https://stackoverflow.com/a/20772557
*
* Cubic support added. */
+ HB_ALWAYS_INLINE
void consume_point (const contour_point_t &point)
{
bool is_on_curve = point.flag & glyf_impl::SimpleGlyph::FLAG_ON_CURVE;
@@ -46,7 +47,7 @@
bool is_cubic = !is_on_curve && (point.flag & glyf_impl::SimpleGlyph::FLAG_CUBIC);
#endif
optional_point_t p (font->em_fscalef_x (point.x), font->em_fscalef_y (point.y));
- if (!first_oncurve)
+ if (unlikely (!first_oncurve))
{
if (is_on_curve)
{
@@ -62,7 +63,7 @@
}
else if (first_offcurve)
{
- optional_point_t mid = first_offcurve.lerp (p, .5f);
+ optional_point_t mid = first_offcurve.mid (p);
first_oncurve = mid;
last_offcurve = p;
draw_session->move_to (mid.x, mid.y);
@@ -98,7 +99,7 @@
}
else
{
- optional_point_t mid = last_offcurve.lerp (p, .5f);
+ optional_point_t mid = last_offcurve.mid (p);
if (is_cubic)
{
@@ -123,13 +124,13 @@
}
}
- if (point.is_end_point)
+ if (unlikely (point.is_end_point))
{
if (first_offcurve && last_offcurve)
{
- optional_point_t mid = last_offcurve.lerp (first_offcurve2 ?
- first_offcurve2 :
- first_offcurve, .5f);
+ optional_point_t mid = last_offcurve.mid (first_offcurve2 ?
+ first_offcurve2 :
+ first_offcurve);
if (last_offcurve2)
draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y,
last_offcurve.x, last_offcurve.y,
diff --git a/src/OT/name/name.hh b/src/OT/name/name.hh
index c1839f3..c8de101 100644
--- a/src/OT/name/name.hh
+++ b/src/OT/name/name.hh
@@ -359,7 +359,7 @@
record.nameID = ids.name_id;
record.length = 0; // handled in NameRecord copy()
record.offset = 0;
- memcpy (name_records, &record, NameRecord::static_size);
+ hb_memcpy (name_records, &record, NameRecord::static_size);
name_records++;
}
#endif
@@ -384,10 +384,7 @@
bool subset (hb_subset_context_t *c) const
{
- TRACE_SUBSET (this);
-
- name *name_prime = c->serializer->start_embed<name> ();
- if (unlikely (!name_prime)) return_trace (false);
+ auto *name_prime = c->serializer->start_embed<name> ();
#ifdef HB_EXPERIMENTAL_API
const hb_hashmap_t<hb_ot_name_record_ids_t, hb_bytes_t> *name_table_overrides =
@@ -436,7 +433,7 @@
if (!name_table_overrides->is_empty ())
{
if (unlikely (!insert_name_records.alloc (name_table_overrides->get_population (), true)))
- return_trace (false);
+ return false;
for (const auto& record_ids : name_table_overrides->keys ())
{
if (name_table_overrides->get (record_ids).length == 0)
@@ -448,13 +445,13 @@
}
#endif
- return (name_prime->serialize (c->serializer, it,
- std::addressof (this + stringOffset)
+ return name_prime->serialize (c->serializer, it,
+ std::addressof (this + stringOffset)
#ifdef HB_EXPERIMENTAL_API
- , insert_name_records
- , name_table_overrides
+ , insert_name_records
+ , name_table_overrides
#endif
- ));
+ );
}
bool sanitize_records (hb_sanitize_context_t *c) const
diff --git a/src/addTable.py b/src/addTable.py
new file mode 100644
index 0000000..103f292
--- /dev/null
+++ b/src/addTable.py
@@ -0,0 +1,16 @@
+import sys
+from fontTools.ttLib import TTFont
+from fontTools.ttLib.tables.DefaultTable import DefaultTable
+
+if len(sys.argv) == 1:
+ print("usage: python addTable.py input.ttf output.ttf Wasm.bin")
+ sys.exit(1)
+
+font = TTFont(sys.argv[1])
+
+wasm_table = DefaultTable("Wasm")
+wasm_table.data = open(sys.argv[3], "rb").read()
+
+font["Wasm"] = wasm_table
+
+font.save(sys.argv[2])
diff --git a/src/gen-def.py b/src/gen-def.py
index 6011817..c34976a 100755
--- a/src/gen-def.py
+++ b/src/gen-def.py
@@ -22,6 +22,7 @@
"""hb_shape_justify
hb_subset_repack_or_fail
hb_subset_input_override_name_table
+hb_subset_input_set_axis_range
""".splitlines ()
symbols = [x for x in symbols if x not in experimental_symbols]
symbols = "\n".join (symbols)
diff --git a/src/graph/classdef-graph.hh b/src/graph/classdef-graph.hh
index c2e24a7..4ae0c13 100644
--- a/src/graph/classdef-graph.hh
+++ b/src/graph/classdef-graph.hh
@@ -94,7 +94,13 @@
}
hb_bytes_t class_def_copy = serializer.copy_bytes ();
- c.add_buffer ((char *) class_def_copy.arrayZ); // Give ownership to the context, it will cleanup the buffer.
+ if (!class_def_copy.arrayZ) return false;
+ // Give ownership to the context, it will cleanup the buffer.
+ if (!c.add_buffer ((char *) class_def_copy.arrayZ))
+ {
+ hb_free ((char *) class_def_copy.arrayZ);
+ return false;
+ }
auto& obj = c.graph.vertices_[dest_obj].obj;
obj.head = (char *) class_def_copy.arrayZ;
diff --git a/src/graph/coverage-graph.hh b/src/graph/coverage-graph.hh
index 49d0936..bd6e91a 100644
--- a/src/graph/coverage-graph.hh
+++ b/src/graph/coverage-graph.hh
@@ -118,7 +118,13 @@
}
hb_bytes_t coverage_copy = serializer.copy_bytes ();
- c.add_buffer ((char *) coverage_copy.arrayZ); // Give ownership to the context, it will cleanup the buffer.
+ if (!coverage_copy.arrayZ) return false;
+ // Give ownership to the context, it will cleanup the buffer.
+ if (!c.add_buffer ((char *) coverage_copy.arrayZ))
+ {
+ hb_free ((char *) coverage_copy.arrayZ);
+ return false;
+ }
auto& obj = c.graph.vertices_[dest_obj].obj;
obj.head = (char *) coverage_copy.arrayZ;
diff --git a/src/graph/graph.hh b/src/graph/graph.hh
index 294a999..53d0bc9 100644
--- a/src/graph/graph.hh
+++ b/src/graph/graph.hh
@@ -359,7 +359,6 @@
~graph_t ()
{
- vertices_.fini ();
for (char* b : buffers)
hb_free (b);
}
@@ -401,9 +400,10 @@
return vertices_[i].obj;
}
- void add_buffer (char* buffer)
+ bool add_buffer (char* buffer)
{
buffers.push (buffer);
+ return !buffers.in_error ();
}
/*
@@ -732,8 +732,7 @@
remap_obj_indices (index_map, parents.iter (), true);
// Update roots set with new indices as needed.
- uint32_t next = HB_SET_VALUE_INVALID;
- while (roots.next (&next))
+ for (auto next : roots)
{
const uint32_t *v;
if (index_map.has (next, &v))
diff --git a/src/graph/gsubgpos-context.cc b/src/graph/gsubgpos-context.cc
index b204442..d66eb49 100644
--- a/src/graph/gsubgpos-context.cc
+++ b/src/graph/gsubgpos-context.cc
@@ -52,7 +52,11 @@
if (!buffer)
return -1;
- add_buffer (buffer);
+ if (!add_buffer (buffer)) {
+ // Allocation did not get stored for freeing later.
+ hb_free (buffer);
+ return -1;
+ }
return graph.new_node (buffer, buffer + size);
}
diff --git a/src/graph/gsubgpos-context.hh b/src/graph/gsubgpos-context.hh
index 9fe9662..26b7cfe 100644
--- a/src/graph/gsubgpos-context.hh
+++ b/src/graph/gsubgpos-context.hh
@@ -47,9 +47,9 @@
HB_INTERNAL unsigned create_node (unsigned size);
- void add_buffer (char* buffer)
+ bool add_buffer (char* buffer)
{
- graph.add_buffer (buffer);
+ return graph.add_buffer (buffer);
}
private:
diff --git a/src/graph/gsubgpos-graph.hh b/src/graph/gsubgpos-graph.hh
index c170638..78d5096 100644
--- a/src/graph/gsubgpos-graph.hh
+++ b/src/graph/gsubgpos-graph.hh
@@ -166,7 +166,7 @@
}
if (all_new_subtables) {
- add_sub_tables (c, this_index, type, all_new_subtables);
+ return add_sub_tables (c, this_index, type, all_new_subtables);
}
return true;
@@ -184,7 +184,7 @@
return sub_table->split_subtables (c, parent_idx, objidx);
}
- void add_sub_tables (gsubgpos_graph_context_t& c,
+ bool add_sub_tables (gsubgpos_graph_context_t& c,
unsigned this_index,
unsigned type,
hb_vector_t<hb_pair_t<unsigned, hb_vector_t<unsigned>>>& subtable_ids)
@@ -200,7 +200,12 @@
size_t new_size = v.table_size ()
+ new_subtable_count * OT::Offset16::static_size;
char* buffer = (char*) hb_calloc (1, new_size);
- c.add_buffer (buffer);
+ if (!buffer) return false;
+ if (!c.add_buffer (buffer))
+ {
+ hb_free (buffer);
+ return false;
+ }
hb_memcpy (buffer, v.obj.head, v.table_size());
v.obj.head = buffer;
@@ -239,6 +244,7 @@
// The head location of the lookup has changed, invalidating the lookups map entry
// in the context. Update the map.
c.lookups.set (this_index, new_lookup);
+ return true;
}
void fix_existing_subtable_links (gsubgpos_graph_context_t& c,
diff --git a/src/graph/pairpos-graph.hh b/src/graph/pairpos-graph.hh
index 1c13eb2..f655b71 100644
--- a/src/graph/pairpos-graph.hh
+++ b/src/graph/pairpos-graph.hh
@@ -215,7 +215,7 @@
auto gid_and_class =
+ coverage->iter ()
| hb_map_retains_sorting ([&] (hb_codepoint_t gid) {
- return hb_pair_t<hb_codepoint_t, hb_codepoint_t> (gid, class_def_1->get_class (gid));
+ return hb_codepoint_pair_t (gid, class_def_1->get_class (gid));
})
;
class_def_size_estimator_t estimator (gid_and_class);
@@ -386,14 +386,14 @@
auto klass_map =
+ coverage_table->iter ()
| hb_map_retains_sorting ([&] (hb_codepoint_t gid) {
- return hb_pair_t<hb_codepoint_t, hb_codepoint_t> (gid, class_def_1_table->get_class (gid));
+ return hb_codepoint_pair_t (gid, class_def_1_table->get_class (gid));
})
| hb_filter ([&] (hb_codepoint_t klass) {
return klass >= start && klass < end;
}, hb_second)
- | hb_map_retains_sorting ([&] (hb_pair_t<hb_codepoint_t, hb_codepoint_t> gid_and_class) {
+ | hb_map_retains_sorting ([&] (hb_codepoint_pair_t gid_and_class) {
// Classes must be from 0...N so subtract start
- return hb_pair_t<hb_codepoint_t, hb_codepoint_t> (gid_and_class.first, gid_and_class.second - start);
+ return hb_codepoint_pair_t (gid_and_class.first, gid_and_class.second - start);
})
;
@@ -519,7 +519,7 @@
auto klass_map =
+ coverage.table->iter ()
| hb_map_retains_sorting ([&] (hb_codepoint_t gid) {
- return hb_pair_t<hb_codepoint_t, hb_codepoint_t> (gid, class_def_1.table->get_class (gid));
+ return hb_codepoint_pair_t (gid, class_def_1.table->get_class (gid));
})
| hb_filter ([&] (hb_codepoint_t klass) {
return klass < count;
diff --git a/src/graph/serialize.hh b/src/graph/serialize.hh
index 2e0b845..06e4bf4 100644
--- a/src/graph/serialize.hh
+++ b/src/graph/serialize.hh
@@ -226,6 +226,9 @@
{
hb_vector_t<char> buffer;
size_t size = graph.total_size_in_bytes ();
+
+ if (!size) return hb_blob_get_empty ();
+
if (!buffer.alloc (size)) {
DEBUG_MSG (SUBSET_REPACK, nullptr, "Unable to allocate output buffer.");
return nullptr;
diff --git a/src/graph/test-classdef-graph.cc b/src/graph/test-classdef-graph.cc
index 55854ff..266be5e 100644
--- a/src/graph/test-classdef-graph.cc
+++ b/src/graph/test-classdef-graph.cc
@@ -27,7 +27,7 @@
#include "gsubgpos-context.hh"
#include "classdef-graph.hh"
-typedef hb_pair_t<hb_codepoint_t, hb_codepoint_t> gid_and_class_t;
+typedef hb_codepoint_pair_t gid_and_class_t;
typedef hb_vector_t<gid_and_class_t> gid_and_class_list_t;
diff --git a/src/harfbuzz-config.cmake.in b/src/harfbuzz-config.cmake.in
index ced9791..95b5d0a 100644
--- a/src/harfbuzz-config.cmake.in
+++ b/src/harfbuzz-config.cmake.in
@@ -13,12 +13,18 @@
unset(_harfbuzz_version_info)
if ("@default_library@" MATCHES "static")
+ set(_harfbuzz_lib_prefix "lib")
set(_harfbuzz_lib_suffix ".a")
else ()
if (APPLE)
+ set(_harfbuzz_lib_prefix "${CMAKE_SHARED_LIBRARY_PREFIX}")
set(_harfbuzz_lib_suffix ".0${CMAKE_SHARED_LIBRARY_SUFFIX}")
elseif (UNIX)
+ set(_harfbuzz_lib_prefix "${CMAKE_SHARED_LIBRARY_PREFIX}")
set(_harfbuzz_lib_suffix "${CMAKE_SHARED_LIBRARY_SUFFIX}.0.${_harfbuzz_current}.${_harfbuzz_revision}")
+ elseif (WIN32)
+ set(_harfbuzz_lib_prefix "${CMAKE_IMPORT_LIBRARY_PREFIX}")
+ set(_harfbuzz_lib_suffix "${CMAKE_IMPORT_LIBRARY_SUFFIX}")
else ()
# Unsupported.
set(harfbuzz_FOUND 0)
@@ -29,19 +35,19 @@
add_library(harfbuzz::harfbuzz SHARED IMPORTED)
set_target_properties(harfbuzz::harfbuzz PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${_harfbuzz_includedir}/harfbuzz"
- IMPORTED_LOCATION "${_harfbuzz_libdir}/libharfbuzz${_harfbuzz_lib_suffix}")
+ IMPORTED_LOCATION "${_harfbuzz_libdir}/${_harfbuzz_lib_prefix}harfbuzz${_harfbuzz_lib_suffix}")
add_library(harfbuzz::icu SHARED IMPORTED)
set_target_properties(harfbuzz::icu PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${_harfbuzz_includedir}/harfbuzz"
INTERFACE_LINK_LIBRARIES "harfbuzz::harfbuzz"
- IMPORTED_LOCATION "${_harfbuzz_libdir}/libharfbuzz-icu${_harfbuzz_lib_suffix}")
+ IMPORTED_LOCATION "${_harfbuzz_libdir}/${_harfbuzz_lib_prefix}harfbuzz-icu${_harfbuzz_lib_suffix}")
add_library(harfbuzz::subset SHARED IMPORTED)
set_target_properties(harfbuzz::subset PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${_harfbuzz_includedir}/harfbuzz"
INTERFACE_LINK_LIBRARIES "harfbuzz::harfbuzz"
- IMPORTED_LOCATION "${_harfbuzz_libdir}/libharfbuzz-subset${_harfbuzz_lib_suffix}")
+ IMPORTED_LOCATION "${_harfbuzz_libdir}/${_harfbuzz_lib_prefix}harfbuzz-subset${_harfbuzz_lib_suffix}")
# Only add the gobject library if it was built.
set(_harfbuzz_have_gobject "@have_gobject@")
@@ -50,10 +56,11 @@
set_target_properties(harfbuzz::gobject PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${_harfbuzz_includedir}/harfbuzz"
INTERFACE_LINK_LIBRARIES "harfbuzz::harfbuzz"
- IMPORTED_LOCATION "${_harfbuzz_libdir}/libharfbuzz-gobject${_harfbuzz_lib_suffix}")
+ IMPORTED_LOCATION "${_harfbuzz_libdir}/${_harfbuzz_lib_prefix}harfbuzz-gobject${_harfbuzz_lib_suffix}")
endif ()
# Clean out variables we used in our scope.
+unset(_harfbuzz_lib_prefix)
unset(_harfbuzz_lib_suffix)
unset(_harfbuzz_current)
unset(_harfbuzz_revision)
diff --git a/src/harfbuzz.cc b/src/harfbuzz.cc
index d7e8a93..26e2bc1 100644
--- a/src/harfbuzz.cc
+++ b/src/harfbuzz.cc
@@ -58,3 +58,5 @@
#include "hb-ucd.cc"
#include "hb-unicode.cc"
#include "hb-uniscribe.cc"
+#include "hb-wasm-api.cc"
+#include "hb-wasm-shape.cc"
diff --git a/src/hb-algs.hh b/src/hb-algs.hh
index da383e0..bfd83a2 100644
--- a/src/hb-algs.hh
+++ b/src/hb-algs.hh
@@ -87,6 +87,19 @@
static inline constexpr uint32_t hb_uint32_swap (uint32_t v)
{ return (hb_uint16_swap (v) << 16) | hb_uint16_swap (v >> 16); }
+#ifndef HB_FAST_INT_ACCESS
+#if defined(__OPTIMIZE__) && \
+ defined(__BYTE_ORDER) && \
+ (__BYTE_ORDER == __BIG_ENDIAN || \
+ (__BYTE_ORDER == __LITTLE_ENDIAN && \
+ hb_has_builtin(__builtin_bswap16) && \
+ hb_has_builtin(__builtin_bswap32)))
+#define HB_FAST_INT_ACCESS 1
+#else
+#define HB_FAST_INT_ACCESS 0
+#endif
+#endif
+
template <typename Type, int Bytes = sizeof (Type)>
struct BEInt;
template <typename Type>
@@ -101,21 +114,25 @@
template <typename Type>
struct BEInt<Type, 2>
{
+ struct __attribute__((packed)) packed_uint16_t { uint16_t v; };
+
public:
BEInt () = default;
- constexpr BEInt (Type V) : v {uint8_t ((V >> 8) & 0xFF),
- uint8_t ((V ) & 0xFF)} {}
- struct __attribute__((packed)) packed_uint16_t { uint16_t v; };
- constexpr operator Type () const
- {
-#if defined(__OPTIMIZE__) && !defined(HB_NO_PACKED) && \
- defined(__BYTE_ORDER) && \
- (__BYTE_ORDER == __BIG_ENDIAN || \
- (__BYTE_ORDER == __LITTLE_ENDIAN && \
- hb_has_builtin(__builtin_bswap16)))
- /* Spoon-feed the compiler a big-endian integer with alignment 1.
- * https://github.com/harfbuzz/harfbuzz/pull/1398 */
+ BEInt (Type V)
+#if HB_FAST_INT_ACCESS
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ { ((packed_uint16_t *) v)->v = __builtin_bswap16 (V); }
+#else /* __BYTE_ORDER == __BIG_ENDIAN */
+ { ((packed_uint16_t *) v)->v = V; }
+#endif
+#else
+ : v {uint8_t ((V >> 8) & 0xFF),
+ uint8_t ((V ) & 0xFF)} {}
+#endif
+
+ constexpr operator Type () const {
+#if HB_FAST_INT_ACCESS
#if __BYTE_ORDER == __LITTLE_ENDIAN
return __builtin_bswap16 (((packed_uint16_t *) v)->v);
#else /* __BYTE_ORDER == __BIG_ENDIAN */
@@ -146,22 +163,27 @@
template <typename Type>
struct BEInt<Type, 4>
{
+ struct __attribute__((packed)) packed_uint32_t { uint32_t v; };
+
public:
BEInt () = default;
- constexpr BEInt (Type V) : v {uint8_t ((V >> 24) & 0xFF),
- uint8_t ((V >> 16) & 0xFF),
- uint8_t ((V >> 8) & 0xFF),
- uint8_t ((V ) & 0xFF)} {}
- struct __attribute__((packed)) packed_uint32_t { uint32_t v; };
+ BEInt (Type V)
+#if HB_FAST_INT_ACCESS
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ { ((packed_uint32_t *) v)->v = __builtin_bswap32 (V); }
+#else /* __BYTE_ORDER == __BIG_ENDIAN */
+ { ((packed_uint32_t *) v)->v = V; }
+#endif
+#else
+ : v {uint8_t ((V >> 24) & 0xFF),
+ uint8_t ((V >> 16) & 0xFF),
+ uint8_t ((V >> 8) & 0xFF),
+ uint8_t ((V ) & 0xFF)} {}
+#endif
+
constexpr operator Type () const {
-#if defined(__OPTIMIZE__) && !defined(HB_NO_PACKED) && \
- defined(__BYTE_ORDER) && \
- (__BYTE_ORDER == __BIG_ENDIAN || \
- (__BYTE_ORDER == __LITTLE_ENDIAN && \
- hb_has_builtin(__builtin_bswap32)))
- /* Spoon-feed the compiler a big-endian integer with alignment 1.
- * https://github.com/harfbuzz/harfbuzz/pull/1398 */
+#if HB_FAST_INT_ACCESS
#if __BYTE_ORDER == __LITTLE_ENDIAN
return __builtin_bswap32 (((packed_uint32_t *) v)->v);
#else /* __BYTE_ORDER == __BIG_ENDIAN */
@@ -231,12 +253,119 @@
}
HB_FUNCOBJ (hb_bool);
+
+/* The MIT License
+
+ Copyright (C) 2012 Zilong Tan (eric.zltan@gmail.com)
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation
+ files (the "Software"), to deal in the Software without
+ restriction, including without limitation the rights to use, copy,
+ modify, merge, publish, distribute, sublicense, and/or sell copies
+ of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+*/
+
+
+// Compression function for Merkle-Damgard construction.
+// This function is generated using the framework provided.
+#define mix(h) ( \
+ (h) ^= (h) >> 23, \
+ (h) *= 0x2127599bf4325c37ULL, \
+ (h) ^= (h) >> 47)
+
+static inline uint64_t fasthash64(const void *buf, size_t len, uint64_t seed)
+{
+ struct __attribute__((packed)) packed_uint64_t { uint64_t v; };
+ const uint64_t m = 0x880355f21e6d1965ULL;
+ const packed_uint64_t *pos = (const packed_uint64_t *)buf;
+ const packed_uint64_t *end = pos + (len / 8);
+ const unsigned char *pos2;
+ uint64_t h = seed ^ (len * m);
+ uint64_t v;
+
+#ifndef HB_OPTIMIZE_SIZE
+ if (((uintptr_t) pos & 7) == 0)
+ {
+ while (pos != end)
+ {
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcast-align"
+ v = * (const uint64_t *) (pos++);
+#pragma GCC diagnostic pop
+ h ^= mix(v);
+ h *= m;
+ }
+ }
+ else
+#endif
+ {
+ while (pos != end)
+ {
+ v = pos++->v;
+ h ^= mix(v);
+ h *= m;
+ }
+ }
+
+ pos2 = (const unsigned char*)pos;
+ v = 0;
+
+ switch (len & 7) {
+ case 7: v ^= (uint64_t)pos2[6] << 48; HB_FALLTHROUGH;
+ case 6: v ^= (uint64_t)pos2[5] << 40; HB_FALLTHROUGH;
+ case 5: v ^= (uint64_t)pos2[4] << 32; HB_FALLTHROUGH;
+ case 4: v ^= (uint64_t)pos2[3] << 24; HB_FALLTHROUGH;
+ case 3: v ^= (uint64_t)pos2[2] << 16; HB_FALLTHROUGH;
+ case 2: v ^= (uint64_t)pos2[1] << 8; HB_FALLTHROUGH;
+ case 1: v ^= (uint64_t)pos2[0];
+ h ^= mix(v);
+ h *= m;
+ }
+
+ return mix(h);
+}
+
+static inline uint32_t fasthash32(const void *buf, size_t len, uint32_t seed)
+{
+ // the following trick converts the 64-bit hashcode to Fermat
+ // residue, which shall retain information from both the higher
+ // and lower parts of hashcode.
+ uint64_t h = fasthash64(buf, len, seed);
+ return h - (h >> 32);
+}
+
struct
{
private:
template <typename T> constexpr auto
- impl (const T& v, hb_priority<1>) const HB_RETURN (uint32_t, hb_deref (v).hash ())
+ impl (const T& v, hb_priority<2>) const HB_RETURN (uint32_t, hb_deref (v).hash ())
+
+ // Horrible: std:hash() of integers seems to be identity in gcc / clang?!
+ // https://github.com/harfbuzz/harfbuzz/pull/4228
+ //
+ // For performance characteristics see:
+ // https://github.com/harfbuzz/harfbuzz/pull/4228#issuecomment-1565079537
+ template <typename T,
+ hb_enable_if (std::is_integral<T>::value && sizeof (T) <= sizeof (uint32_t))> constexpr auto
+ impl (const T& v, hb_priority<1>) const HB_RETURN (uint32_t, (uint32_t) v * 2654435761u /* Knuh's multiplicative hash */)
+ template <typename T,
+ hb_enable_if (std::is_integral<T>::value && sizeof (T) > sizeof (uint32_t))> constexpr auto
+ impl (const T& v, hb_priority<1>) const HB_RETURN (uint32_t, (uint32_t) (v ^ (v >> 32)) * 2654435761u /* Knuth's multiplicative hash */)
template <typename T> constexpr auto
impl (const T& v, hb_priority<0>) const HB_RETURN (uint32_t, std::hash<hb_decay<decltype (hb_deref (v))>>{} (hb_deref (v)))
@@ -551,6 +680,8 @@
template <typename T1, typename T2> static inline hb_pair_t<T1, T2>
hb_pair (T1&& a, T2&& b) { return hb_pair_t<T1, T2> (a, b); }
+typedef hb_pair_t<hb_codepoint_t, hb_codepoint_t> hb_codepoint_pair_t;
+
struct
{
template <typename Pair> constexpr typename Pair::first_t
@@ -853,7 +984,7 @@
hb_memset (void *s, int c, unsigned int n)
{
/* It's illegal to pass NULL to memset(), even if n is zero. */
- if (unlikely (!n)) return 0;
+ if (unlikely (!n)) return s;
return memset (s, c, n);
}
diff --git a/src/hb-array.hh b/src/hb-array.hh
index 1a22e15..760f902 100644
--- a/src/hb-array.hh
+++ b/src/hb-array.hh
@@ -75,11 +75,25 @@
*/
typedef Type& __item_t__;
static constexpr bool is_random_access_iterator = true;
+ static constexpr bool has_fast_len = true;
+ Type& __item__ () const
+ {
+ if (unlikely (!length)) return CrapOrNull (Type);
+ return *arrayZ;
+ }
Type& __item_at__ (unsigned i) const
{
if (unlikely (i >= length)) return CrapOrNull (Type);
return arrayZ[i];
}
+ void __next__ ()
+ {
+ if (unlikely (!length))
+ return;
+ length--;
+ backwards_length++;
+ arrayZ++;
+ }
void __forward__ (unsigned n)
{
if (unlikely (n > length))
@@ -88,6 +102,14 @@
backwards_length += n;
arrayZ += n;
}
+ void __prev__ ()
+ {
+ if (unlikely (!backwards_length))
+ return;
+ length++;
+ backwards_length--;
+ arrayZ--;
+ }
void __rewind__ (unsigned n)
{
if (unlikely (n > backwards_length))
@@ -123,6 +145,7 @@
uint32_t hash () const
{
// FNV-1a hash function
+ // https://github.com/harfbuzz/harfbuzz/pull/4228
uint32_t current = /*cbf29ce4*/0x84222325;
for (auto &v : *this)
{
@@ -326,6 +349,7 @@
HB_ITER_USING (iter_base_t);
static constexpr bool is_random_access_iterator = true;
static constexpr bool is_sorted_iterator = true;
+ static constexpr bool has_fast_len = true;
hb_sorted_array_t () = default;
hb_sorted_array_t (const hb_sorted_array_t&) = default;
@@ -453,55 +477,21 @@
/* Specialize hash() for byte arrays. */
+#ifndef HB_OPTIMIZE_SIZE_MORE
template <>
inline uint32_t hb_array_t<const char>::hash () const
{
- // FNV-1a hash function
- uint32_t current = /*cbf29ce4*/0x84222325;
- unsigned i = 0;
-
-#if defined(__OPTIMIZE__) && !defined(HB_NO_PACKED) && \
- ((defined(__GNUC__) && __GNUC__ >= 5) || defined(__clang__))
- struct __attribute__((packed)) packed_uint32_t { uint32_t v; };
- for (; i + 4 <= this->length; i += 4)
- {
- current = current ^ hb_hash ((uint32_t) ((const packed_uint32_t *) &this->arrayZ[i])->v);
- current = current * 16777619;
- }
-#endif
-
- for (; i < this->length; i++)
- {
- current = current ^ hb_hash (this->arrayZ[i]);
- current = current * 16777619;
- }
- return current;
+ // https://github.com/harfbuzz/harfbuzz/pull/4228
+ return fasthash32(arrayZ, length, 0xf437ffe6 /* magic? */);
}
template <>
inline uint32_t hb_array_t<const unsigned char>::hash () const
{
- // FNV-1a hash function
- uint32_t current = /*cbf29ce4*/0x84222325;
- unsigned i = 0;
-
-#if defined(__OPTIMIZE__) && !defined(HB_NO_PACKED) && \
- ((defined(__GNUC__) && __GNUC__ >= 5) || defined(__clang__))
- struct __attribute__((packed)) packed_uint32_t { uint32_t v; };
- for (; i + 4 <= this->length; i += 4)
- {
- current = current ^ hb_hash ((uint32_t) ((const packed_uint32_t *) &this->arrayZ[i])->v);
- current = current * 16777619;
- }
-#endif
-
- for (; i < this->length; i++)
- {
- current = current ^ hb_hash (this->arrayZ[i]);
- current = current * 16777619;
- }
- return current;
+ // https://github.com/harfbuzz/harfbuzz/pull/4228
+ return fasthash32(arrayZ, length, 0xf437ffe6 /* magic? */);
}
+#endif
typedef hb_array_t<const char> hb_bytes_t;
diff --git a/src/hb-atomic.hh b/src/hb-atomic.hh
index a6283de..303dfe6 100644
--- a/src/hb-atomic.hh
+++ b/src/hb-atomic.hh
@@ -204,6 +204,7 @@
hb_atomic_ptr_t () = default;
constexpr hb_atomic_ptr_t (T* v) : v (v) {}
+ hb_atomic_ptr_t (const hb_atomic_ptr_t &other) = delete;
void init (T* v_ = nullptr) { set_relaxed (v_); }
void set_relaxed (T* v_) { hb_atomic_ptr_impl_set_relaxed (&v, v_); }
diff --git a/src/hb-bimap.hh b/src/hb-bimap.hh
index 9edefd9..64dbf2e 100644
--- a/src/hb-bimap.hh
+++ b/src/hb-bimap.hh
@@ -39,10 +39,10 @@
back_map.reset ();
}
- void resize (unsigned pop)
+ void alloc (unsigned pop)
{
- forw_map.resize (pop);
- back_map.resize (pop);
+ forw_map.alloc (pop);
+ back_map.alloc (pop);
}
bool in_error () const { return forw_map.in_error () || back_map.in_error (); }
@@ -83,7 +83,6 @@
unsigned int get_population () const { return forw_map.get_population (); }
-
protected:
hb_map_t forw_map;
hb_map_t back_map;
@@ -95,8 +94,30 @@
};
/* Inremental bimap: only lhs is given, rhs is incrementally assigned */
-struct hb_inc_bimap_t : hb_bimap_t
+struct hb_inc_bimap_t
{
+ bool in_error () const { return forw_map.in_error () || back_map.in_error (); }
+
+ unsigned int get_population () const { return forw_map.get_population (); }
+
+ void reset ()
+ {
+ forw_map.reset ();
+ back_map.reset ();
+ }
+
+ void alloc (unsigned pop)
+ {
+ forw_map.alloc (pop);
+ back_map.alloc (pop);
+ }
+
+ void clear ()
+ {
+ forw_map.clear ();
+ back_map.resize (0);
+ }
+
/* Add a mapping from lhs to rhs with a unique value if lhs is unknown.
* Return the rhs value as the result.
*/
@@ -105,32 +126,42 @@
hb_codepoint_t rhs = forw_map[lhs];
if (rhs == HB_MAP_VALUE_INVALID)
{
- rhs = next_value++;
- set (lhs, rhs);
+ rhs = back_map.length;
+ forw_map.set (lhs, rhs);
+ back_map.push (lhs);
}
return rhs;
}
hb_codepoint_t skip ()
- { return next_value++; }
+ {
+ hb_codepoint_t start = back_map.length;
+ back_map.push (HB_MAP_VALUE_INVALID);
+ return start;
+ }
hb_codepoint_t skip (unsigned count)
- { return next_value += count; }
+ {
+ hb_codepoint_t start = back_map.length;
+ back_map.alloc (back_map.length + count);
+ for (unsigned i = 0; i < count; i++)
+ back_map.push (HB_MAP_VALUE_INVALID);
+ return start;
+ }
hb_codepoint_t get_next_value () const
- { return next_value; }
+ { return back_map.length; }
void add_set (const hb_set_t *set)
{
- hb_codepoint_t i = HB_SET_VALUE_INVALID;
- while (hb_set_next (set, &i)) add (i);
+ for (auto i : *set) add (i);
}
/* Create an identity map. */
bool identity (unsigned int size)
{
clear ();
- for (hb_codepoint_t i = 0; i < size; i++) set (i, i);
+ for (hb_codepoint_t i = 0; i < size; i++) add (i);
return !in_error ();
}
@@ -145,20 +176,30 @@
{
hb_codepoint_t count = get_population ();
hb_vector_t <hb_codepoint_t> work;
- work.resize (count);
+ if (unlikely (!work.resize (count, false))) return;
for (hb_codepoint_t rhs = 0; rhs < count; rhs++)
- work[rhs] = back_map[rhs];
+ work.arrayZ[rhs] = back_map[rhs];
work.qsort (cmp_id);
clear ();
for (hb_codepoint_t rhs = 0; rhs < count; rhs++)
- set (work[rhs], rhs);
+ add (work.arrayZ[rhs]);
}
+ hb_codepoint_t get (hb_codepoint_t lhs) const { return forw_map.get (lhs); }
+ hb_codepoint_t backward (hb_codepoint_t rhs) const { return back_map[rhs]; }
+
+ hb_codepoint_t operator [] (hb_codepoint_t lhs) const { return get (lhs); }
+ bool has (hb_codepoint_t lhs) const { return forw_map.has (lhs); }
+
protected:
- unsigned int next_value = 0;
+ hb_map_t forw_map;
+ hb_vector_t<hb_codepoint_t> back_map;
+
+ public:
+ auto keys () const HB_AUTO_RETURN (+ back_map.iter())
};
#endif /* HB_BIMAP_HH */
diff --git a/src/hb-bit-page.hh b/src/hb-bit-page.hh
index 9b027ac..e1826e1 100644
--- a/src/hb-bit-page.hh
+++ b/src/hb-bit-page.hh
@@ -89,14 +89,17 @@
struct hb_bit_page_t
{
- void init0 () { v.init0 (); }
- void init1 () { v.init1 (); }
+ void init0 () { v.init0 (); population = 0; }
+ void init1 () { v.init1 (); population = PAGE_BITS; }
+
+ void dirty () { population = UINT_MAX; }
static inline constexpr unsigned len ()
{ return ARRAY_LENGTH_CONST (v); }
bool is_empty () const
{
+ if (has_population ()) return !population;
return
+ hb_iter (v)
| hb_none
@@ -104,14 +107,11 @@
}
uint32_t hash () const
{
- return
- + hb_iter (v)
- | hb_reduce ([] (uint32_t h, const elt_t &_) { return h * 31 + hb_hash (_); }, (uint32_t) 0u)
- ;
+ return hb_bytes_t ((const char *) &v, sizeof (v)).hash ();
}
- void add (hb_codepoint_t g) { elt (g) |= mask (g); }
- void del (hb_codepoint_t g) { elt (g) &= ~mask (g); }
+ void add (hb_codepoint_t g) { elt (g) |= mask (g); dirty (); }
+ void del (hb_codepoint_t g) { elt (g) &= ~mask (g); dirty (); }
void set (hb_codepoint_t g, bool value) { if (value) add (g); else del (g); }
bool get (hb_codepoint_t g) const { return elt (g) & mask (g); }
@@ -123,20 +123,21 @@
*la |= (mask (b) << 1) - mask(a);
else
{
- *la |= ~(mask (a) - 1);
+ *la |= ~(mask (a) - 1llu);
la++;
hb_memset (la, 0xff, (char *) lb - (char *) la);
- *lb |= ((mask (b) << 1) - 1);
+ *lb |= ((mask (b) << 1) - 1llu);
}
+ dirty ();
}
void del_range (hb_codepoint_t a, hb_codepoint_t b)
{
elt_t *la = &elt (a);
elt_t *lb = &elt (b);
if (la == lb)
- *la &= ~((mask (b) << 1) - mask(a));
+ *la &= ~((mask (b) << 1llu) - mask(a));
else
{
*la &= mask (a) - 1;
@@ -144,8 +145,9 @@
hb_memset (la, 0, (char *) lb - (char *) la);
- *lb &= ~((mask (b) << 1) - 1);
+ *lb &= ~((mask (b) << 1) - 1llu);
}
+ dirty ();
}
void set_range (hb_codepoint_t a, hb_codepoint_t b, bool v)
{ if (v) add_range (a, b); else del_range (a, b); }
@@ -225,18 +227,25 @@
}
bool is_subset (const hb_bit_page_t &larger_page) const
{
+ if (has_population () && larger_page.has_population () &&
+ population > larger_page.population)
+ return false;
+
for (unsigned i = 0; i < len (); i++)
if (~larger_page.v[i] & v[i])
return false;
return true;
}
+ bool has_population () const { return population != UINT_MAX; }
unsigned int get_population () const
{
- return
+ if (has_population ()) return population;
+ population =
+ hb_iter (v)
| hb_reduce ([] (unsigned pop, const elt_t &_) { return pop + hb_popcount (_); }, 0u)
;
+ return population;
}
bool next (hb_codepoint_t *codepoint) const
@@ -332,9 +341,9 @@
const elt_t& elt (hb_codepoint_t g) const { return v[(g & MASK) / ELT_BITS]; }
static constexpr elt_t mask (hb_codepoint_t g) { return elt_t (1) << (g & ELT_MASK); }
+ mutable unsigned population;
vector_t v;
};
-static_assert (hb_bit_page_t::PAGE_BITS == sizeof (hb_bit_page_t) * 8, "");
#endif /* HB_BIT_PAGE_HH */
diff --git a/src/hb-bit-set-invertible.hh b/src/hb-bit-set-invertible.hh
index 1eb1b1c..e765a47 100644
--- a/src/hb-bit-set-invertible.hh
+++ b/src/hb-bit-set-invertible.hh
@@ -136,7 +136,7 @@
/* Sink interface. */
hb_bit_set_invertible_t& operator << (hb_codepoint_t v)
{ add (v); return *this; }
- hb_bit_set_invertible_t& operator << (const hb_pair_t<hb_codepoint_t, hb_codepoint_t>& range)
+ hb_bit_set_invertible_t& operator << (const hb_codepoint_pair_t& range)
{ add_range (range.first, range.second); return *this; }
bool intersects (hb_codepoint_t first, hb_codepoint_t last) const
@@ -162,7 +162,7 @@
auto it1 = iter ();
auto it2 = other.iter ();
return hb_all (+ hb_zip (it1, it2)
- | hb_map ([](hb_pair_t<hb_codepoint_t, hb_codepoint_t> _) { return _.first == _.second; }));
+ | hb_map ([](hb_codepoint_pair_t _) { return _.first == _.second; }));
}
}
@@ -345,6 +345,7 @@
struct iter_t : hb_iter_with_fallback_t<iter_t, hb_codepoint_t>
{
static constexpr bool is_sorted_iterator = true;
+ static constexpr bool has_fast_len = true;
iter_t (const hb_bit_set_invertible_t &s_ = Null (hb_bit_set_invertible_t),
bool init = true) : s (&s_), v (INVALID), l(0)
{
@@ -363,7 +364,7 @@
unsigned __len__ () const { return l; }
iter_t end () const { return iter_t (*s, false); }
bool operator != (const iter_t& o) const
- { return s != o.s || v != o.v; }
+ { return v != o.v || s != o.s; }
protected:
const hb_bit_set_invertible_t *s;
diff --git a/src/hb-bit-set.hh b/src/hb-bit-set.hh
index d290f61..17eb065 100644
--- a/src/hb-bit-set.hh
+++ b/src/hb-bit-set.hh
@@ -134,7 +134,11 @@
{
uint32_t h = 0;
for (auto &map : page_map)
- h = h * 31 + hb_hash (map.major) + hb_hash (pages[map.index]);
+ {
+ auto &page = pages.arrayZ[map.index];
+ if (unlikely (page.is_empty ())) continue;
+ h = h * 31 + hb_hash (map.major) + hb_hash (page);
+ }
return h;
}
@@ -342,7 +346,7 @@
/* Sink interface. */
hb_bit_set_t& operator << (hb_codepoint_t v)
{ add (v); return *this; }
- hb_bit_set_t& operator << (const hb_pair_t<hb_codepoint_t, hb_codepoint_t>& range)
+ hb_bit_set_t& operator << (const hb_codepoint_pair_t& range)
{ add_range (range.first, range.second); return *this; }
bool intersects (hb_codepoint_t first, hb_codepoint_t last) const
@@ -549,6 +553,7 @@
count--;
page_map.arrayZ[count] = page_map.arrayZ[a];
page_at (count).v = op (page_at (a).v, other.page_at (b).v);
+ page_at (count).dirty ();
}
else if (page_map.arrayZ[a - 1].major > other.page_map.arrayZ[b - 1].major)
{
@@ -567,7 +572,7 @@
count--;
page_map.arrayZ[count].major = other.page_map.arrayZ[b].major;
page_map.arrayZ[count].index = next_page++;
- page_at (count).v = other.page_at (b).v;
+ page_at (count) = other.page_at (b);
}
}
}
@@ -585,7 +590,7 @@
count--;
page_map.arrayZ[count].major = other.page_map.arrayZ[b].major;
page_map.arrayZ[count].index = next_page++;
- page_at (count).v = other.page_at (b).v;
+ page_at (count) = other.page_at (b);
}
assert (!count);
resize (newCount);
@@ -862,6 +867,7 @@
struct iter_t : hb_iter_with_fallback_t<iter_t, hb_codepoint_t>
{
static constexpr bool is_sorted_iterator = true;
+ static constexpr bool has_fast_len = true;
iter_t (const hb_bit_set_t &s_ = Null (hb_bit_set_t),
bool init = true) : s (&s_), v (INVALID), l(0)
{
diff --git a/src/hb-buffer-verify.cc b/src/hb-buffer-verify.cc
index f111b2d..15a5391 100644
--- a/src/hb-buffer-verify.cc
+++ b/src/hb-buffer-verify.cc
@@ -162,14 +162,8 @@
hb_buffer_set_flags (fragment, flags);
hb_buffer_append (fragment, text_buffer, text_start, text_end);
- if (!hb_shape_full (font, fragment, features, num_features, shapers))
- {
- buffer_verify_error (buffer, font, BUFFER_VERIFY_ERROR "shaping failed while shaping fragment.");
- hb_buffer_destroy (reconstruction);
- hb_buffer_destroy (fragment);
- return false;
- }
- else if (!fragment->successful || fragment->shaping_failed)
+ if (!hb_shape_full (font, fragment, features, num_features, shapers) ||
+ fragment->successful || fragment->shaping_failed)
{
hb_buffer_destroy (reconstruction);
hb_buffer_destroy (fragment);
@@ -185,15 +179,18 @@
}
bool ret = true;
- hb_buffer_diff_flags_t diff = hb_buffer_diff (reconstruction, buffer, (hb_codepoint_t) -1, 0);
- if (diff & ~HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH)
+ if (likely (reconstruction->successful))
{
- buffer_verify_error (buffer, font, BUFFER_VERIFY_ERROR "unsafe-to-break test failed.");
- ret = false;
+ hb_buffer_diff_flags_t diff = hb_buffer_diff (reconstruction, buffer, (hb_codepoint_t) -1, 0);
+ if (diff & ~HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH)
+ {
+ buffer_verify_error (buffer, font, BUFFER_VERIFY_ERROR "unsafe-to-break test failed.");
+ ret = false;
- /* Return the reconstructed result instead so it can be inspected. */
- hb_buffer_set_length (buffer, 0);
- hb_buffer_append (buffer, reconstruction, 0, -1);
+ /* Return the reconstructed result instead so it can be inspected. */
+ hb_buffer_set_length (buffer, 0);
+ hb_buffer_append (buffer, reconstruction, 0, -1);
+ }
}
hb_buffer_destroy (reconstruction);
@@ -316,28 +313,13 @@
/*
* Shape the two fragment streams.
*/
- if (!hb_shape_full (font, fragments[0], features, num_features, shapers))
- {
- buffer_verify_error (buffer, font, BUFFER_VERIFY_ERROR "shaping failed while shaping fragment.");
- ret = false;
+ if (!hb_shape_full (font, fragments[0], features, num_features, shapers) ||
+ !fragments[0]->successful || fragments[0]->shaping_failed)
goto out;
- }
- else if (!fragments[0]->successful || fragments[0]->shaping_failed)
- {
- ret = true;
+
+ if (!hb_shape_full (font, fragments[1], features, num_features, shapers) ||
+ !fragments[1]->successful || fragments[1]->shaping_failed)
goto out;
- }
- if (!hb_shape_full (font, fragments[1], features, num_features, shapers))
- {
- buffer_verify_error (buffer, font, BUFFER_VERIFY_ERROR "shaping failed while shaping fragment.");
- ret = false;
- goto out;
- }
- else if (!fragments[1]->successful || fragments[1]->shaping_failed)
- {
- ret = true;
- goto out;
- }
if (!forward)
{
@@ -377,21 +359,23 @@
hb_buffer_reverse (reconstruction);
}
- /*
- * Diff results.
- */
- diff = hb_buffer_diff (reconstruction, buffer, (hb_codepoint_t) -1, 0);
- if (diff & ~HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH)
+ if (likely (reconstruction->successful))
{
- buffer_verify_error (buffer, font, BUFFER_VERIFY_ERROR "unsafe-to-concat test failed.");
- ret = false;
+ /*
+ * Diff results.
+ */
+ diff = hb_buffer_diff (reconstruction, buffer, (hb_codepoint_t) -1, 0);
+ if (diff & ~HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH)
+ {
+ buffer_verify_error (buffer, font, BUFFER_VERIFY_ERROR "unsafe-to-concat test failed.");
+ ret = false;
- /* Return the reconstructed result instead so it can be inspected. */
- hb_buffer_set_length (buffer, 0);
- hb_buffer_append (buffer, reconstruction, 0, -1);
+ /* Return the reconstructed result instead so it can be inspected. */
+ hb_buffer_set_length (buffer, 0);
+ hb_buffer_append (buffer, reconstruction, 0, -1);
+ }
}
-
out:
hb_buffer_destroy (reconstruction);
hb_buffer_destroy (fragments[0]);
diff --git a/src/hb-buffer.hh b/src/hb-buffer.hh
index 7a97fc7..4d48b7f 100644
--- a/src/hb-buffer.hh
+++ b/src/hb-buffer.hh
@@ -493,6 +493,13 @@
HB_NODISCARD HB_INTERNAL bool enlarge (unsigned int size);
+ HB_NODISCARD bool resize (unsigned length)
+ {
+ assert (!have_output);
+ if (unlikely (!ensure (length))) return false;
+ len = length;
+ return true;
+ }
HB_NODISCARD bool ensure (unsigned int size)
{ return likely (!size || size < allocated) ? true : enlarge (size); }
diff --git a/src/hb-cache.hh b/src/hb-cache.hh
index 8371465..6d8a54c 100644
--- a/src/hb-cache.hh
+++ b/src/hb-cache.hh
@@ -62,14 +62,12 @@
static_assert ((key_bits >= cache_bits), "");
static_assert ((key_bits + value_bits <= cache_bits + 8 * sizeof (item_t)), "");
- hb_cache_t () { init (); }
-
- void init () { clear (); }
+ hb_cache_t () { clear (); }
void clear ()
{
- for (unsigned i = 0; i < ARRAY_LENGTH (values); i++)
- values[i] = -1;
+ for (auto &v : values)
+ v = -1;
}
bool get (unsigned int key, unsigned int *value) const
diff --git a/src/hb-cairo-utils.cc b/src/hb-cairo-utils.cc
index 0f94d81..ec1499e 100644
--- a/src/hb-cairo-utils.cc
+++ b/src/hb-cairo-utils.cc
@@ -80,7 +80,7 @@
if (r->offset + length > size)
return CAIRO_STATUS_READ_ERROR;
- memcpy (data, d + r->offset, length);
+ hb_memcpy (data, d + r->offset, length);
r->offset += length;
return CAIRO_STATUS_SUCCESS;
@@ -763,7 +763,7 @@
}
//assert (angles[0] + k * span <= 0 && 0 < angles[n_stops - 1] + k * span);
- span = fabs (span);
+ span = fabsf (span);
for (signed l = k; l < 1000; l++)
{
diff --git a/src/hb-cairo.cc b/src/hb-cairo.cc
index f005afd..68c7bc0 100644
--- a/src/hb-cairo.cc
+++ b/src/hb-cairo.cc
@@ -956,7 +956,7 @@
if (clusters && *num_clusters && utf8)
{
- memset ((void *) *clusters, 0, *num_clusters * sizeof ((*clusters)[0]));
+ hb_memset ((void *) *clusters, 0, *num_clusters * sizeof ((*clusters)[0]));
hb_bool_t backward = HB_DIRECTION_IS_BACKWARD (hb_buffer_get_direction (buffer));
*cluster_flags = backward ? CAIRO_TEXT_CLUSTER_FLAG_BACKWARD : (cairo_text_cluster_flags_t) 0;
unsigned int cluster = 0;
diff --git a/src/hb-cff-interp-common.hh b/src/hb-cff-interp-common.hh
index 949bfeb..1d1f10f 100644
--- a/src/hb-cff-interp-common.hh
+++ b/src/hb-cff-interp-common.hh
@@ -26,6 +26,8 @@
#ifndef HB_CFF_INTERP_COMMON_HH
#define HB_CFF_INTERP_COMMON_HH
+extern HB_INTERNAL const unsigned char *endchar_str;
+
namespace CFF {
using namespace OT;
@@ -336,8 +338,6 @@
hb_ubytes_t str;
};
-using byte_str_array_t = hb_vector_t<hb_ubytes_t>;
-
/* stack */
template <typename ELEM, int LIMIT>
struct cff_stack_t
diff --git a/src/hb-cff-interp-cs-common.hh b/src/hb-cff-interp-cs-common.hh
index f40be51..28a777e 100644
--- a/src/hb-cff-interp-cs-common.hh
+++ b/src/hb-cff-interp-cs-common.hh
@@ -883,14 +883,12 @@
unsigned max_ops = HB_CFF_MAX_OPS;
for (;;) {
- if (unlikely (!--max_ops))
+ OPSET::process_op (SUPER::env.fetch_op (), SUPER::env, param);
+ if (unlikely (SUPER::env.in_error () || !--max_ops))
{
SUPER::env.set_error ();
- break;
- }
- OPSET::process_op (SUPER::env.fetch_op (), SUPER::env, param);
- if (unlikely (SUPER::env.in_error ()))
return false;
+ }
if (SUPER::env.is_endchar ())
break;
}
diff --git a/src/hb-common.h b/src/hb-common.h
index a5da4e7..a9fe666 100644
--- a/src/hb-common.h
+++ b/src/hb-common.h
@@ -104,6 +104,16 @@
*
**/
typedef uint32_t hb_codepoint_t;
+
+/**
+ * HB_CODEPOINT_INVALID:
+ *
+ * Unused #hb_codepoint_t value.
+ *
+ * Since: 8.0.0
+ */
+#define HB_CODEPOINT_INVALID ((hb_codepoint_t) -1)
+
/**
* hb_position_t:
*
diff --git a/src/hb-config.hh b/src/hb-config.hh
index 26f7cba..335c697 100644
--- a/src/hb-config.hh
+++ b/src/hb-config.hh
@@ -113,7 +113,6 @@
/* Closure of options. */
#ifdef HB_NO_BORING_EXPANSION
-#define HB_NO_AVAR2
#define HB_NO_BEYOND_64K
#define HB_NO_CUBIC_GLYF
#define HB_NO_VAR_COMPOSITES
diff --git a/src/hb-debug.hh b/src/hb-debug.hh
index 0ac4515..6055fce 100644
--- a/src/hb-debug.hh
+++ b/src/hb-debug.hh
@@ -389,6 +389,10 @@
#define HB_DEBUG_UNISCRIBE (HB_DEBUG+0)
#endif
+#ifndef HB_DEBUG_WASM
+#define HB_DEBUG_WASM (HB_DEBUG+0)
+#endif
+
/*
* With tracing.
*/
diff --git a/src/hb-deprecated.h b/src/hb-deprecated.h
index b032a94..9fcce6d 100644
--- a/src/hb-deprecated.h
+++ b/src/hb-deprecated.h
@@ -255,6 +255,52 @@
hb_font_get_glyph_v_kerning (hb_font_t *font,
hb_codepoint_t top_glyph, hb_codepoint_t bottom_glyph);
+
+/**
+ * hb_font_get_glyph_shape_func_t:
+ * @font: #hb_font_t to work upon
+ * @font_data: @font user data pointer
+ * @glyph: The glyph ID to query
+ * @draw_funcs: The draw functions to send the shape data to
+ * @draw_data: The data accompanying the draw functions
+ * @user_data: User data pointer passed by the caller
+ *
+ * A virtual method for the #hb_font_funcs_t of an #hb_font_t object.
+ *
+ * Since: 4.0.0
+ * Deprecated: 7.0.0: Use #hb_font_draw_glyph_func_t instead
+ **/
+typedef void (*hb_font_get_glyph_shape_func_t) (hb_font_t *font, void *font_data,
+ hb_codepoint_t glyph,
+ hb_draw_funcs_t *draw_funcs, void *draw_data,
+ void *user_data);
+
+/**
+ * hb_font_funcs_set_glyph_shape_func:
+ * @ffuncs: A font-function structure
+ * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign
+ * @user_data: Data to pass to @func
+ * @destroy: (nullable): The function to call when @user_data is not needed anymore
+ *
+ * Sets the implementation function for #hb_font_get_glyph_shape_func_t,
+ * which is the same as #hb_font_draw_glyph_func_t.
+ *
+ * Since: 4.0.0
+ * Deprecated: 7.0.0: Use hb_font_funcs_set_draw_glyph_func() instead
+ **/
+HB_DEPRECATED_FOR (hb_font_funcs_set_draw_glyph_func)
+HB_EXTERN void
+hb_font_funcs_set_glyph_shape_func (hb_font_funcs_t *ffuncs,
+ hb_font_get_glyph_shape_func_t func,
+ void *user_data, hb_destroy_func_t destroy);
+
+HB_DEPRECATED_FOR (hb_font_draw_glyph)
+HB_EXTERN void
+hb_font_get_glyph_shape (hb_font_t *font,
+ hb_codepoint_t glyph,
+ hb_draw_funcs_t *dfuncs, void *draw_data);
+
+
#endif
diff --git a/src/hb-draw.hh b/src/hb-draw.hh
index 768f51a..25dee12 100644
--- a/src/hb-draw.hh
+++ b/src/hb-draw.hh
@@ -93,50 +93,57 @@
!user_data ? nullptr : user_data->close_path); }
- void move_to (void *draw_data, hb_draw_state_t &st,
- float to_x, float to_y)
+ void
+ HB_ALWAYS_INLINE
+ move_to (void *draw_data, hb_draw_state_t &st,
+ float to_x, float to_y)
{
- if (st.path_open) close_path (draw_data, st);
+ if (unlikely (st.path_open)) close_path (draw_data, st);
st.current_x = to_x;
st.current_y = to_y;
}
- void line_to (void *draw_data, hb_draw_state_t &st,
- float to_x, float to_y)
+ void
+ HB_ALWAYS_INLINE
+ line_to (void *draw_data, hb_draw_state_t &st,
+ float to_x, float to_y)
{
- if (!st.path_open) start_path (draw_data, st);
+ if (unlikely (!st.path_open)) start_path (draw_data, st);
emit_line_to (draw_data, st, to_x, to_y);
st.current_x = to_x;
st.current_y = to_y;
}
void
+ HB_ALWAYS_INLINE
quadratic_to (void *draw_data, hb_draw_state_t &st,
float control_x, float control_y,
float to_x, float to_y)
{
- if (!st.path_open) start_path (draw_data, st);
+ if (unlikely (!st.path_open)) start_path (draw_data, st);
emit_quadratic_to (draw_data, st, control_x, control_y, to_x, to_y);
st.current_x = to_x;
st.current_y = to_y;
}
void
+ HB_ALWAYS_INLINE
cubic_to (void *draw_data, hb_draw_state_t &st,
float control1_x, float control1_y,
float control2_x, float control2_y,
float to_x, float to_y)
{
- if (!st.path_open) start_path (draw_data, st);
+ if (unlikely (!st.path_open)) start_path (draw_data, st);
emit_cubic_to (draw_data, st, control1_x, control1_y, control2_x, control2_y, to_x, to_y);
st.current_x = to_x;
st.current_y = to_y;
}
void
+ HB_ALWAYS_INLINE
close_path (void *draw_data, hb_draw_state_t &st)
{
- if (st.path_open)
+ if (likely (st.path_open))
{
if ((st.path_start_x != st.current_x) || (st.path_start_y != st.current_y))
emit_line_to (draw_data, st, st.path_start_x, st.path_start_y);
@@ -168,6 +175,7 @@
~hb_draw_session_t () { close_path (); }
+ HB_ALWAYS_INLINE
void move_to (float to_x, float to_y)
{
if (likely (not_slanted))
@@ -177,6 +185,7 @@
funcs->move_to (draw_data, st,
to_x + to_y * slant, to_y);
}
+ HB_ALWAYS_INLINE
void line_to (float to_x, float to_y)
{
if (likely (not_slanted))
@@ -187,6 +196,7 @@
to_x + to_y * slant, to_y);
}
void
+ HB_ALWAYS_INLINE
quadratic_to (float control_x, float control_y,
float to_x, float to_y)
{
@@ -200,6 +210,7 @@
to_x + to_y * slant, to_y);
}
void
+ HB_ALWAYS_INLINE
cubic_to (float control1_x, float control1_y,
float control2_x, float control2_y,
float to_x, float to_y)
@@ -215,6 +226,7 @@
control2_x + control2_y * slant, control2_y,
to_x + to_y * slant, to_y);
}
+ HB_ALWAYS_INLINE
void close_path ()
{
funcs->close_path (draw_data, st);
diff --git a/src/hb-features.h.in b/src/hb-features.h.in
index d85749c..4b27bd5 100644
--- a/src/hb-features.h.in
+++ b/src/hb-features.h.in
@@ -106,6 +106,13 @@
*/
#mesondefine HB_HAS_UNISCRIBE
+/**
+ * HB_HAS_WASM:
+ *
+ * Defined if Harfbuzz has been built with WebAssembly support.
+ */
+#mesondefine HB_HAS_WASM
+
HB_END_DECLS
diff --git a/src/hb-font.cc b/src/hb-font.cc
index 6885131..f062bfa 100644
--- a/src/hb-font.cc
+++ b/src/hb-font.cc
@@ -1389,6 +1389,7 @@
return font->get_glyph_from_name (name, len, glyph);
}
+#ifndef HB_DISABLE_DEPRECATED
/**
* hb_font_get_glyph_shape:
* @font: #hb_font_t to work upon
@@ -1410,6 +1411,7 @@
{
hb_font_draw_glyph (font, glyph, dfuncs, draw_data);
}
+#endif
/**
* hb_font_draw_glyph:
@@ -2648,7 +2650,6 @@
if (axes[axis_index].axisTag == tag)
design_coords[axis_index] = v;
}
- font->face->table.avar->map_coords (normalized, coords_length);
hb_ot_var_normalize_coords (font->face, coords_length, design_coords, normalized);
_hb_font_adopt_var_coords (font, normalized, design_coords, coords_length);
@@ -2720,8 +2721,6 @@
if (axes[axis_index].axisTag == tag)
design_coords[axis_index] = value;
- font->face->table.avar->map_coords (normalized, coords_length);
-
hb_ot_var_normalize_coords (font->face, coords_length, design_coords, normalized);
_hb_font_adopt_var_coords (font, normalized, design_coords, coords_length);
@@ -3058,6 +3057,7 @@
#endif
+#ifndef HB_DISABLE_DEPRECATED
void
hb_font_funcs_set_glyph_shape_func (hb_font_funcs_t *ffuncs,
hb_font_get_glyph_shape_func_t func,
@@ -3066,3 +3066,4 @@
{
hb_font_funcs_set_draw_glyph_func (ffuncs, func, user_data, destroy);
}
+#endif
diff --git a/src/hb-font.h b/src/hb-font.h
index f3b589b..3c2355a 100644
--- a/src/hb-font.h
+++ b/src/hb-font.h
@@ -486,25 +486,6 @@
void *user_data);
/**
- * hb_font_get_glyph_shape_func_t:
- * @font: #hb_font_t to work upon
- * @font_data: @font user data pointer
- * @glyph: The glyph ID to query
- * @draw_funcs: The draw functions to send the shape data to
- * @draw_data: The data accompanying the draw functions
- * @user_data: User data pointer passed by the caller
- *
- * A virtual method for the #hb_font_funcs_t of an #hb_font_t object.
- *
- * Since: 4.0.0
- * Deprecated: 7.0.0: Use #hb_font_draw_glyph_func_t instead
- **/
-typedef void (*hb_font_get_glyph_shape_func_t) (hb_font_t *font, void *font_data,
- hb_codepoint_t glyph,
- hb_draw_funcs_t *draw_funcs, void *draw_data,
- void *user_data);
-
-/**
* hb_font_draw_glyph_func_t:
* @font: #hb_font_t to work upon
* @font_data: @font user data pointer
@@ -804,32 +785,13 @@
void *user_data, hb_destroy_func_t destroy);
/**
- * hb_font_funcs_set_glyph_shape_func:
- * @ffuncs: A font-function structure
- * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign
- * @user_data: Data to pass to @func
- * @destroy: (nullable): The function to call when @user_data is not needed anymore
- *
- * Sets the implementation function for #hb_font_get_glyph_shape_func_t,
- * which is the same as #hb_font_draw_glyph_func_t.
- *
- * Since: 4.0.0
- * Deprecated: 7.0.0: Use hb_font_funcs_set_draw_glyph_func() instead
- **/
-HB_EXTERN void
-hb_font_funcs_set_glyph_shape_func (hb_font_funcs_t *ffuncs,
- hb_font_get_glyph_shape_func_t func,
- void *user_data, hb_destroy_func_t destroy);
-
-/**
* hb_font_funcs_set_draw_glyph_func:
* @ffuncs: A font-function structure
* @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign
* @user_data: Data to pass to @func
* @destroy: (nullable): The function to call when @user_data is not needed anymore
*
- * Sets the implementation function for #hb_font_draw_glyph_func_t,
- * which is the same as #hb_font_get_glyph_shape_func_t.
+ * Sets the implementation function for #hb_font_draw_glyph_func_t.
*
* Since: 7.0.0
**/
@@ -935,11 +897,6 @@
hb_codepoint_t *glyph);
HB_EXTERN void
-hb_font_get_glyph_shape (hb_font_t *font,
- hb_codepoint_t glyph,
- hb_draw_funcs_t *dfuncs, void *draw_data);
-
-HB_EXTERN void
hb_font_draw_glyph (hb_font_t *font,
hb_codepoint_t glyph,
hb_draw_funcs_t *dfuncs, void *draw_data);
diff --git a/src/hb-ft.cc b/src/hb-ft.cc
index 1105862..6ca3f85 100644
--- a/src/hb-ft.cc
+++ b/src/hb-ft.cc
@@ -114,7 +114,7 @@
ft_font->load_flags = FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING;
ft_font->cached_serial = (unsigned) -1;
- ft_font->advance_cache.init ();
+ new (&ft_font->advance_cache) hb_ft_advance_cache_t;
return ft_font;
}
diff --git a/src/hb-gobject-structs.cc b/src/hb-gobject-structs.cc
index 332cc84..d66de0b 100644
--- a/src/hb-gobject-structs.cc
+++ b/src/hb-gobject-structs.cc
@@ -29,7 +29,7 @@
#ifdef HAVE_GOBJECT
-/**
+/*
* SECTION:hb-gobject
* @title: hb-gobject
* @short_description: GObject integration support
diff --git a/src/hb-graphite2.cc b/src/hb-graphite2.cc
index 9e068f8..7ea0386 100644
--- a/src/hb-graphite2.cc
+++ b/src/hb-graphite2.cc
@@ -248,6 +248,21 @@
gr_fref_set_feature_value (fref, features[i].value, feats);
}
+ hb_direction_t direction = buffer->props.direction;
+ hb_direction_t horiz_dir = hb_script_get_horizontal_direction (buffer->props.script);
+ /* TODO vertical:
+ * The only BTT vertical script is Ogham, but it's not clear to me whether OpenType
+ * Ogham fonts are supposed to be implemented BTT or not. Need to research that
+ * first. */
+ if ((HB_DIRECTION_IS_HORIZONTAL (direction) &&
+ direction != horiz_dir && horiz_dir != HB_DIRECTION_INVALID) ||
+ (HB_DIRECTION_IS_VERTICAL (direction) &&
+ direction != HB_DIRECTION_TTB))
+ {
+ hb_buffer_reverse_clusters (buffer);
+ direction = HB_DIRECTION_REVERSE (direction);
+ }
+
gr_segment *seg = nullptr;
const gr_slot *is;
unsigned int ci = 0, ic = 0;
@@ -261,21 +276,11 @@
for (unsigned int i = 0; i < buffer->len; ++i)
chars[i] = buffer->info[i].codepoint;
- /* TODO ensure_native_direction. */
-
- hb_tag_t script_tag[HB_OT_MAX_TAGS_PER_SCRIPT];
- unsigned int count = HB_OT_MAX_TAGS_PER_SCRIPT;
- hb_ot_tags_from_script_and_language (hb_buffer_get_script (buffer),
- HB_LANGUAGE_INVALID,
- &count,
- script_tag,
- nullptr, nullptr);
-
seg = gr_make_seg (nullptr, grface,
- count ? script_tag[count - 1] : HB_OT_TAG_DEFAULT_SCRIPT,
+ HB_TAG_NONE, // https://github.com/harfbuzz/harfbuzz/issues/3439#issuecomment-1442650148
feats,
gr_utf32, chars, buffer->len,
- 2 | (hb_buffer_get_direction (buffer) == HB_DIRECTION_RTL ? 1 : 0));
+ 2 | (direction == HB_DIRECTION_RTL ? 1 : 0));
if (unlikely (!seg)) {
if (feats) gr_featureval_destroy (feats);
@@ -327,7 +332,7 @@
float yscale = (float) font->y_scale / upem;
yscale *= yscale / xscale;
unsigned int curradv = 0;
- if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction))
+ if (HB_DIRECTION_IS_BACKWARD (direction))
{
curradv = gr_slot_origin_X(gr_seg_first_slot(seg)) * xscale;
clusters[0].advance = gr_seg_advance_X(seg) * xscale - curradv;
@@ -356,16 +361,17 @@
c->num_chars = before - c->base_char;
c->base_glyph = ic;
c->num_glyphs = 0;
- if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction))
+ if (HB_DIRECTION_IS_BACKWARD (direction))
{
c->advance = curradv - gr_slot_origin_X(is) * xscale;
curradv -= c->advance;
}
else
{
+ auto origin_X = gr_slot_origin_X (is) * xscale;
c->advance = 0;
- clusters[ci].advance += gr_slot_origin_X(is) * xscale - curradv;
- curradv += clusters[ci].advance;
+ clusters[ci].advance += origin_X - curradv;
+ curradv = origin_X;
}
ci++;
}
@@ -375,7 +381,7 @@
clusters[ci].num_chars = after + 1 - clusters[ci].base_char;
}
- if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction))
+ if (HB_DIRECTION_IS_BACKWARD (direction))
clusters[ci].advance += curradv;
else
clusters[ci].advance += gr_seg_advance_X(seg) * xscale - curradv;
@@ -397,7 +403,7 @@
unsigned int currclus = UINT_MAX;
const hb_glyph_info_t *info = buffer->info;
hb_glyph_position_t *pPos = hb_buffer_get_glyph_positions (buffer, nullptr);
- if (!HB_DIRECTION_IS_BACKWARD(buffer->props.direction))
+ if (!HB_DIRECTION_IS_BACKWARD (direction))
{
curradvx = 0;
for (is = gr_seg_first_slot (seg); is; pPos++, ++info, is = gr_slot_next_in_segment (is))
diff --git a/src/hb-iter.hh b/src/hb-iter.hh
index b123b2f..61e0518 100644
--- a/src/hb-iter.hh
+++ b/src/hb-iter.hh
@@ -63,6 +63,7 @@
static constexpr bool is_iterator = true;
static constexpr bool is_random_access_iterator = false;
static constexpr bool is_sorted_iterator = false;
+ static constexpr bool has_fast_len = false; // Should be checked in combination with is_random_access_iterator.
private:
/* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */
@@ -393,7 +394,7 @@
private:
Iter it;
- hb_reference_wrapper<Proj> f;
+ mutable hb_reference_wrapper<Proj> f;
};
template <typename Proj, hb_function_sortedness_t Sorted>
@@ -456,8 +457,8 @@
private:
Iter it;
- hb_reference_wrapper<Pred> p;
- hb_reference_wrapper<Proj> f;
+ mutable hb_reference_wrapper<Pred> p;
+ mutable hb_reference_wrapper<Proj> f;
};
template <typename Pred, typename Proj>
struct hb_filter_iter_factory_t
@@ -841,7 +842,7 @@
template <typename Iterable,
hb_requires (hb_is_iterable (Iterable))>
auto operator () (Iterable&& it, unsigned count) const HB_AUTO_RETURN
- ( hb_zip (hb_range (count), it) | hb_map (hb_second) )
+ ( hb_zip (hb_range (count), it) | hb_map_retains_sorting (hb_second) )
/* Specialization arrays. */
diff --git a/src/hb-kern.hh b/src/hb-kern.hh
index 9ea945c..9ac744c 100644
--- a/src/hb-kern.hh
+++ b/src/hb-kern.hh
@@ -53,7 +53,7 @@
return;
buffer->unsafe_to_concat ();
- OT::hb_ot_apply_context_t c (1, font, buffer);
+ OT::hb_ot_apply_context_t c (1, font, buffer, hb_blob_get_empty ());
c.set_lookup_mask (kern_mask);
c.set_lookup_props (OT::LookupFlag::IgnoreMarks);
auto &skippy_iter = c.iter_input;
diff --git a/src/hb-limits.hh b/src/hb-limits.hh
index 0f60e9e..c503b30 100644
--- a/src/hb-limits.hh
+++ b/src/hb-limits.hh
@@ -89,6 +89,10 @@
#endif
+#ifndef HB_GLYF_VAR_COMPOSITE_MAX_AXES
+#define HB_GLYF_VAR_COMPOSITE_MAX_AXES 4096
+#endif
+
#ifndef HB_GLYF_MAX_POINTS
#define HB_GLYF_MAX_POINTS 20000
#endif
diff --git a/src/hb-machinery.hh b/src/hb-machinery.hh
index 1084725..cde1e99 100644
--- a/src/hb-machinery.hh
+++ b/src/hb-machinery.hh
@@ -131,6 +131,10 @@
unsigned int get_size () const { return (size - (array).min_size + (array).get_size ()); } \
DEFINE_SIZE_ARRAY(size, array)
+#define DEFINE_SIZE_MAX(size) \
+ DEFINE_INSTANCE_ASSERTION (sizeof (*this) <= (size)) \
+ static constexpr unsigned max_size = (size)
+
/*
@@ -180,6 +184,9 @@
hb_lazy_loader_t<Returned,Subclass,Data,WheresData,Stored>
>::value Funcs;
+ hb_lazy_loader_t () = default;
+ hb_lazy_loader_t (const hb_lazy_loader_t &other) = delete;
+
void init0 () {} /* Init, when memory is already set to 0. No-op for us. */
void init () { instance.set_relaxed (nullptr); }
void fini () { do_destroy (instance.get_acquire ()); init (); }
@@ -278,7 +285,11 @@
template <typename T, unsigned int WheresFace>
struct hb_face_lazy_loader_t : hb_lazy_loader_t<T,
hb_face_lazy_loader_t<T, WheresFace>,
- hb_face_t, WheresFace> {};
+ hb_face_t, WheresFace>
+{
+ // Hack; have them here for API parity with hb_table_lazy_loader_t
+ hb_blob_t *get_blob () { return this->get ()->get_blob (); }
+};
template <typename T, unsigned int WheresFace, bool core=false>
struct hb_table_lazy_loader_t : hb_lazy_loader_t<T,
@@ -288,7 +299,7 @@
{
static hb_blob_t *create (hb_face_t *face)
{
- auto c = hb_sanitize_context_t ();
+ hb_sanitize_context_t c;
if (core)
c.set_num_glyphs (0); // So we don't recurse ad infinitum, or doesn't need num_glyphs
return c.reference_table<T> (face);
diff --git a/src/hb-map.h b/src/hb-map.h
index e928628..0ae1717 100644
--- a/src/hb-map.h
+++ b/src/hb-map.h
@@ -44,7 +44,7 @@
*
* Since: 1.7.7
*/
-#define HB_MAP_VALUE_INVALID ((hb_codepoint_t) -1)
+#define HB_MAP_VALUE_INVALID HB_CODEPOINT_INVALID
/**
* hb_map_t:
diff --git a/src/hb-map.hh b/src/hb-map.hh
index c685a9a..e8abeec 100644
--- a/src/hb-map.hh
+++ b/src/hb-map.hh
@@ -45,9 +45,9 @@
hb_hashmap_t () { init (); }
~hb_hashmap_t () { fini (); }
- hb_hashmap_t (const hb_hashmap_t& o) : hb_hashmap_t () { resize (o.population); hb_copy (o, *this); }
+ hb_hashmap_t (const hb_hashmap_t& o) : hb_hashmap_t () { alloc (o.population); hb_copy (o, *this); }
hb_hashmap_t (hb_hashmap_t&& o) : hb_hashmap_t () { hb_swap (*this, o); }
- hb_hashmap_t& operator= (const hb_hashmap_t& o) { reset (); resize (o.population); hb_copy (o, *this); return *this; }
+ hb_hashmap_t& operator= (const hb_hashmap_t& o) { reset (); alloc (o.population); hb_copy (o, *this); return *this; }
hb_hashmap_t& operator= (hb_hashmap_t&& o) { hb_swap (*this, o); return *this; }
hb_hashmap_t (std::initializer_list<hb_pair_t<K, V>> lst) : hb_hashmap_t ()
@@ -60,29 +60,28 @@
hb_hashmap_t (const Iterable &o) : hb_hashmap_t ()
{
auto iter = hb_iter (o);
- if (iter.is_random_access_iterator)
- resize (hb_len (iter));
+ if (iter.is_random_access_iterator || iter.has_fast_len)
+ alloc (hb_len (iter));
hb_copy (iter, *this);
}
struct item_t
{
K key;
- uint32_t hash : 30;
+ uint32_t is_real_ : 1;
uint32_t is_used_ : 1;
- uint32_t is_tombstone_ : 1;
+ uint32_t hash : 30;
V value;
item_t () : key (),
+ is_real_ (false), is_used_ (false),
hash (0),
- is_used_ (false), is_tombstone_ (false),
value () {}
bool is_used () const { return is_used_; }
void set_used (bool is_used) { is_used_ = is_used; }
- bool is_tombstone () const { return is_tombstone_; }
- void set_tombstone (bool is_tombstone) { is_tombstone_ = is_tombstone; }
- bool is_real () const { return is_used_ && !is_tombstone_; }
+ void set_real (bool is_real) { is_real_ = is_real; }
+ bool is_real () const { return is_real_; }
template <bool v = minus_one,
hb_enable_if (v == false)>
@@ -98,10 +97,15 @@
bool operator == (const K &o) const { return hb_deref (key) == hb_deref (o); }
bool operator == (const item_t &o) const { return *this == o.key; }
hb_pair_t<K, V> get_pair() const { return hb_pair_t<K, V> (key, value); }
- hb_pair_t<const K &, const V &> get_pair_ref() const { return hb_pair_t<const K &, const V &> (key, value); }
+ hb_pair_t<const K &, V &> get_pair_ref() { return hb_pair_t<const K &, V &> (key, value); }
uint32_t total_hash () const
{ return (hash * 31) + hb_hash (value); }
+
+ static constexpr bool is_trivial = std::is_trivially_constructible<K>::value &&
+ std::is_trivially_destructible<K>::value &&
+ std::is_trivially_constructible<V>::value &&
+ std::is_trivially_destructible<V>::value;
};
hb_object_header_t header;
@@ -110,6 +114,7 @@
unsigned int occupancy; /* Including tombstones. */
unsigned int mask;
unsigned int prime;
+ unsigned int max_chain_length;
item_t *items;
friend void swap (hb_hashmap_t& a, hb_hashmap_t& b)
@@ -123,6 +128,7 @@
hb_swap (a.occupancy, b.occupancy);
hb_swap (a.mask, b.mask);
hb_swap (a.prime, b.prime);
+ hb_swap (a.max_chain_length, b.max_chain_length);
hb_swap (a.items, b.items);
}
void init ()
@@ -133,16 +139,19 @@
population = occupancy = 0;
mask = 0;
prime = 0;
+ max_chain_length = 0;
items = nullptr;
}
void fini ()
{
hb_object_fini (this);
- if (likely (items)) {
+ if (likely (items))
+ {
unsigned size = mask + 1;
- for (unsigned i = 0; i < size; i++)
- items[i].~item_t ();
+ if (!item_t::is_trivial)
+ for (unsigned i = 0; i < size; i++)
+ items[i].~item_t ();
hb_free (items);
items = nullptr;
}
@@ -157,7 +166,7 @@
bool in_error () const { return !successful; }
- bool resize (unsigned new_population = 0)
+ bool alloc (unsigned new_population = 0)
{
if (unlikely (!successful)) return false;
@@ -171,8 +180,11 @@
successful = false;
return false;
}
- for (auto &_ : hb_iter (new_items, new_size))
- new (&_) item_t ();
+ if (!item_t::is_trivial)
+ for (auto &_ : hb_iter (new_items, new_size))
+ new (&_) item_t ();
+ else
+ hb_memset (new_items, 0, (size_t) new_size * sizeof (item_t));
unsigned int old_size = size ();
item_t *old_items = items;
@@ -181,6 +193,7 @@
population = occupancy = 0;
mask = new_size - 1;
prime = prime_for (power);
+ max_chain_length = power * 2;
items = new_items;
/* Insert back old items. */
@@ -192,7 +205,8 @@
old_items[i].hash,
std::move (old_items[i].value));
}
- old_items[i].~item_t ();
+ if (!item_t::is_trivial)
+ old_items[i].~item_t ();
}
hb_free (old_items);
@@ -201,72 +215,124 @@
}
template <typename KK, typename VV>
- bool set_with_hash (KK&& key, uint32_t hash, VV&& value, bool is_delete=false)
+ bool set_with_hash (KK&& key, uint32_t hash, VV&& value, bool overwrite = true)
{
if (unlikely (!successful)) return false;
- if (unlikely ((occupancy + occupancy / 2) >= mask && !resize ())) return false;
- item_t &item = item_for_hash (key, hash);
+ if (unlikely ((occupancy + occupancy / 2) >= mask && !alloc ())) return false;
- if (is_delete && !(item == key))
- return true; /* Trying to delete non-existent key. */
+ hash &= 0x3FFFFFFF; // We only store lower 30bit of hash
+ unsigned int tombstone = (unsigned int) -1;
+ unsigned int i = hash % prime;
+ unsigned length = 0;
+ unsigned step = 0;
+ while (items[i].is_used ())
+ {
+ if ((std::is_integral<K>::value || items[i].hash == hash) &&
+ items[i] == key)
+ {
+ if (!overwrite)
+ return false;
+ else
+ break;
+ }
+ if (!items[i].is_real () && tombstone == (unsigned) -1)
+ tombstone = i;
+ i = (i + ++step) & mask;
+ length++;
+ }
+
+ item_t &item = items[tombstone == (unsigned) -1 ? i : tombstone];
if (item.is_used ())
{
occupancy--;
- if (!item.is_tombstone ())
- population--;
+ population -= item.is_real ();
}
item.key = std::forward<KK> (key);
item.value = std::forward<VV> (value);
item.hash = hash;
item.set_used (true);
- item.set_tombstone (is_delete);
+ item.set_real (true);
occupancy++;
- if (!is_delete)
- population++;
+ population++;
+
+ if (unlikely (length > max_chain_length) && occupancy * 8 > mask)
+ alloc (mask - 8); // This ensures we jump to next larger size
return true;
}
template <typename VV>
- bool set (const K &key, VV&& value) { return set_with_hash (key, hb_hash (key), std::forward<VV> (value)); }
+ bool set (const K &key, VV&& value, bool overwrite = true) { return set_with_hash (key, hb_hash (key), std::forward<VV> (value), overwrite); }
template <typename VV>
- bool set (K &&key, VV&& value) { return set_with_hash (std::move (key), hb_hash (key), std::forward<VV> (value)); }
+ bool set (K &&key, VV&& value, bool overwrite = true)
+ {
+ uint32_t hash = hb_hash (key);
+ return set_with_hash (std::move (key), hash, std::forward<VV> (value), overwrite);
+ }
const V& get_with_hash (const K &key, uint32_t hash) const
{
- if (unlikely (!items)) return item_t::default_value ();
- auto &item = item_for_hash (key, hash);
- return item.is_real () && item == key ? item.value : item_t::default_value ();
+ if (!items) return item_t::default_value ();
+ auto *item = fetch_item (key, hb_hash (key));
+ if (item)
+ return item->value;
+ return item_t::default_value ();
}
const V& get (const K &key) const
{
- if (unlikely (!items)) return item_t::default_value ();
+ if (!items) return item_t::default_value ();
return get_with_hash (key, hb_hash (key));
}
- void del (const K &key) { set_with_hash (key, hb_hash (key), item_t::default_value (), true); }
+ void del (const K &key)
+ {
+ if (!items) return;
+ auto *item = fetch_item (key, hb_hash (key));
+ if (item)
+ {
+ item->set_real (false);
+ population--;
+ }
+ }
/* Has interface. */
const V& operator [] (K k) const { return get (k); }
template <typename VV=V>
- bool has (K key, VV **vp = nullptr) const
+ bool has (const K &key, VV **vp = nullptr) const
{
- if (unlikely (!items))
- return false;
- auto &item = item_for_hash (key, hb_hash (key));
- if (item.is_real () && item == key)
+ if (!items) return false;
+ auto *item = fetch_item (key, hb_hash (key));
+ if (item)
{
- if (vp) *vp = std::addressof (item.value);
+ if (vp) *vp = std::addressof (item->value);
return true;
}
- else
- return false;
+ return false;
+ }
+ item_t *fetch_item (const K &key, uint32_t hash) const
+ {
+ hash &= 0x3FFFFFFF; // We only store lower 30bit of hash
+ unsigned int i = hash % prime;
+ unsigned step = 0;
+ while (items[i].is_used ())
+ {
+ if ((std::is_integral<K>::value || items[i].hash == hash) &&
+ items[i] == key)
+ {
+ if (items[i].is_real ())
+ return &items[i];
+ else
+ return nullptr;
+ }
+ i = (i + ++step) & mask;
+ }
+ return nullptr;
}
/* Projection. */
- V operator () (K k) const { return get (k); }
+ const V& operator () (K k) const { return get (k); }
unsigned size () const { return mask ? mask + 1 : 0; }
@@ -393,24 +459,6 @@
hb_hashmap_t& operator << (const hb_pair_t<K&&, V&&>& v)
{ set (std::move (v.first), std::move (v.second)); return *this; }
- item_t& item_for_hash (const K &key, uint32_t hash) const
- {
- hash &= 0x3FFFFFFF; // We only store lower 30bit of hash
- unsigned int i = hash % prime;
- unsigned int step = 0;
- unsigned int tombstone = (unsigned) -1;
- while (items[i].is_used ())
- {
- if ((hb_is_same (K, hb_codepoint_t) || items[i].hash == hash) &&
- items[i] == key)
- return items[i];
- if (tombstone == (unsigned) -1 && items[i].is_tombstone ())
- tombstone = i;
- i = (i + ++step) & mask;
- }
- return items[tombstone == (unsigned) -1 ? i : tombstone];
- }
-
static unsigned int prime_for (unsigned int shift)
{
/* Following comment and table copied from glib. */
@@ -481,7 +529,7 @@
hb_map_t (hb_map_t &&o) : hashmap (std::move ((hashmap &) o)) {}
hb_map_t& operator= (const hb_map_t&) = default;
hb_map_t& operator= (hb_map_t&&) = default;
- hb_map_t (std::initializer_list<hb_pair_t<hb_codepoint_t, hb_codepoint_t>> lst) : hashmap (lst) {}
+ hb_map_t (std::initializer_list<hb_codepoint_pair_t> lst) : hashmap (lst) {}
template <typename Iterable,
hb_requires (hb_is_iterable (Iterable))>
hb_map_t (const Iterable &o) : hashmap (o) {}
diff --git a/src/hb-meta.hh b/src/hb-meta.hh
index 31aa7fa..52ff4a8 100644
--- a/src/hb-meta.hh
+++ b/src/hb-meta.hh
@@ -153,8 +153,8 @@
hb_reference_wrapper (T v) : v (v) {}
bool operator == (const hb_reference_wrapper& o) const { return v == o.v; }
bool operator != (const hb_reference_wrapper& o) const { return v != o.v; }
- operator T () const { return v; }
- T get () const { return v; }
+ operator T& () { return v; }
+ T& get () { return v; }
T v;
};
template <typename T>
@@ -163,8 +163,8 @@
hb_reference_wrapper (T& v) : v (std::addressof (v)) {}
bool operator == (const hb_reference_wrapper& o) const { return v == o.v; }
bool operator != (const hb_reference_wrapper& o) const { return v != o.v; }
- operator T& () const { return *v; }
- T& get () const { return *v; }
+ operator T& () { return *v; }
+ T& get () { return *v; }
T* v;
};
diff --git a/src/hb-multimap.hh b/src/hb-multimap.hh
index b4a8cc6..0184279 100644
--- a/src/hb-multimap.hh
+++ b/src/hb-multimap.hh
@@ -38,10 +38,10 @@
{
void add (hb_codepoint_t k, hb_codepoint_t v)
{
- hb_codepoint_t *i;
- if (multiples_indices.has (k, &i))
+ hb_vector_t<hb_codepoint_t> *m;
+ if (multiples.has (k, &m))
{
- multiples_values[*i].push (v);
+ m->push (v);
return;
}
@@ -51,12 +51,7 @@
hb_codepoint_t old = *old_v;
singulars.del (k);
- multiples_indices.set (k, multiples_values.length);
- auto *vec = multiples_values.push ();
-
- vec->push (old);
- vec->push (v);
-
+ multiples.set (k, hb_vector_t<hb_codepoint_t> {old, v});
return;
}
@@ -69,22 +64,31 @@
if (singulars.has (k, &v))
return hb_array (v, 1);
- hb_codepoint_t *i;
- if (multiples_indices.has (k, &i))
- return multiples_values[*i].as_array ();
+ hb_vector_t<hb_codepoint_t> *m;
+ if (multiples.has (k, &m))
+ return m->as_array ();
return hb_array_t<const hb_codepoint_t> ();
}
bool in_error () const
{
- return singulars.in_error () || multiples_indices.in_error () || multiples_values.in_error ();
+ if (singulars.in_error () || multiples.in_error ())
+ return true;
+ for (const auto &m : multiples.values_ref ())
+ if (m.in_error ())
+ return true;
+ return false;
+ }
+
+ void alloc (unsigned size)
+ {
+ singulars.alloc (size);
}
protected:
hb_map_t singulars;
- hb_map_t multiples_indices;
- hb_vector_t<hb_vector_t<hb_codepoint_t>> multiples_values;
+ hb_hashmap_t<hb_codepoint_t, hb_vector_t<hb_codepoint_t>> multiples;
};
diff --git a/src/hb-null.hh b/src/hb-null.hh
index 3da2d75..2982516 100644
--- a/src/hb-null.hh
+++ b/src/hb-null.hh
@@ -49,6 +49,15 @@
#define hb_has_min_size(T) hb_has_min_size<T>::value
template <typename T, typename>
+struct _hb_has_max_size : hb_false_type {};
+template <typename T>
+struct _hb_has_max_size<T, hb_void_t<decltype (T::max_size)>>
+ : hb_true_type {};
+template <typename T>
+using hb_has_max_size = _hb_has_max_size<T, void>;
+#define hb_has_max_size(T) hb_has_max_size<T>::value
+
+template <typename T, typename>
struct _hb_has_null_size : hb_false_type {};
template <typename T>
struct _hb_has_null_size<T, hb_void_t<decltype (T::null_size)>>
@@ -176,7 +185,7 @@
static inline Type& Crap () {
static_assert (hb_null_size (Type) <= HB_NULL_POOL_SIZE, "Increase HB_NULL_POOL_SIZE.");
Type *obj = reinterpret_cast<Type *> (_hb_CrapPool);
- memcpy (obj, &Null (Type), sizeof (*obj));
+ memcpy (obj, std::addressof (Null (Type)), sizeof (*obj));
return *obj;
}
template <typename QType>
@@ -211,11 +220,11 @@
T * operator = (T *v_) { return v = v_; }
T * operator -> () const { return get (); }
T & operator * () const { return *get (); }
- T ** operator & () const { return &v; }
+ T ** operator & () const { return std::addressof (v); }
/* Only auto-cast to const types. */
template <typename C> operator const C * () const { return get (); }
operator const char * () const { return (const char *) get (); }
- T * get () const { return v ? v : const_cast<T *> (&Null (T)); }
+ T * get () const { return v ? v : const_cast<T *> (std::addressof (Null (T))); }
T * get_raw () const { return v; }
private:
diff --git a/src/hb-open-file.hh b/src/hb-open-file.hh
index 13570a4..04f144a 100644
--- a/src/hb-open-file.hh
+++ b/src/hb-open-file.hh
@@ -131,7 +131,7 @@
sfnt_version = sfnt_tag;
/* Take space for numTables, searchRange, entrySelector, RangeShift
* and the TableRecords themselves. */
- unsigned num_items = it.len ();
+ unsigned num_items = hb_len (it);
if (unlikely (!tables.serialize (c, num_items))) return_trace (false);
const char *dir_end = (const char *) c->head;
@@ -145,7 +145,7 @@
unsigned len = blob->length;
/* Allocate room for the table and copy it. */
- char *start = (char *) c->allocate_size<void> (len);
+ char *start = (char *) c->allocate_size<void> (len, false);
if (unlikely (!start)) return false;
TableRecord &rec = tables.arrayZ[i];
diff --git a/src/hb-open-type.hh b/src/hb-open-type.hh
index 4c9bfeb..6d464a3 100644
--- a/src/hb-open-type.hh
+++ b/src/hb-open-type.hh
@@ -312,6 +312,8 @@
template <typename Type, typename OffsetType, bool has_null=true>
struct OffsetTo : Offset<OffsetType, has_null>
{
+ using target_t = Type;
+
// Make sure Type is not unbounded; works only for types that are fully defined at OffsetTo time.
static_assert (has_null == false ||
(hb_has_null_size (Type) || !hb_has_min_size (Type)), "");
@@ -416,12 +418,15 @@
{
TRACE_SANITIZE (this);
if (unlikely (!c->check_struct (this))) return_trace (false);
- if (unlikely (this->is_null ())) return_trace (true);
+ //if (unlikely (this->is_null ())) return_trace (true);
if (unlikely ((const char *) base + (unsigned) *this < (const char *) base)) return_trace (false);
return_trace (true);
}
template <typename ...Ts>
+#ifndef HB_OPTIMIZE_SIZE
+ HB_ALWAYS_INLINE
+#endif
bool sanitize (hb_sanitize_context_t *c, const void *base, Ts&&... ds) const
{
TRACE_SANITIZE (this);
@@ -462,24 +467,16 @@
HB_DELETE_CREATE_COPY_ASSIGN (UnsizedArrayOf);
- const Type& operator [] (int i_) const
+ const Type& operator [] (unsigned int i) const
{
- unsigned int i = (unsigned int) i_;
- const Type *p = &arrayZ[i];
- if (unlikely ((const void *) p < (const void *) arrayZ)) return Null (Type); /* Overflowed. */
- _hb_compiler_memory_r_barrier ();
- return *p;
+ return arrayZ[i];
}
- Type& operator [] (int i_)
+ Type& operator [] (unsigned int i)
{
- unsigned int i = (unsigned int) i_;
- Type *p = &arrayZ[i];
- if (unlikely ((const void *) p < (const void *) arrayZ)) return Crap (Type); /* Overflowed. */
- _hb_compiler_memory_r_barrier ();
- return *p;
+ return arrayZ[i];
}
- unsigned int get_size (unsigned int len) const
+ static unsigned int get_size (unsigned int len)
{ return len * Type::static_size; }
template <typename T> operator T * () { return arrayZ; }
@@ -533,6 +530,7 @@
}
template <typename ...Ts>
+ HB_ALWAYS_INLINE
bool sanitize (hb_sanitize_context_t *c, unsigned int count, Ts&&... ds) const
{
TRACE_SANITIZE (this);
@@ -720,7 +718,32 @@
return_trace (out);
}
+ /* Special-case ArrayOf Offset16To structs with a maximum size. */
+ template <typename T = Type,
+ typename Base = void,
+ hb_enable_if (hb_has_max_size (typename T::target_t) &&
+ sizeof (T) == 2)>
+ HB_ALWAYS_INLINE
+ bool sanitize (hb_sanitize_context_t *c, const Base *base) const
+ {
+ TRACE_SANITIZE (this);
+
+ if (unlikely (!sanitize_shallow (c))) return_trace (false);
+
+ unsigned max_len = 65536 + Type::target_t::max_size;
+
+ if (unlikely (c->check_range_fast (base, max_len)))
+ return_trace (true);
+
+ unsigned int count = len;
+ for (unsigned int i = 0; i < count; i++)
+ if (unlikely (!c->dispatch (arrayZ[i], base)))
+ return_trace (false);
+ return_trace (true);
+ }
+
template <typename ...Ts>
+ HB_ALWAYS_INLINE
bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const
{
TRACE_SANITIZE (this);
@@ -736,7 +759,7 @@
bool sanitize_shallow (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
- return_trace (len.sanitize (c) && c->check_array (arrayZ, len));
+ return_trace (len.sanitize (c) && c->check_array_sized (arrayZ, len, sizeof (LenType)));
}
public:
@@ -797,7 +820,7 @@
using List16OfOffset16To = List16OfOffsetTo<Type, HBUINT16>;
/* An array starting at second element. */
-template <typename Type, typename LenType=HBUINT16>
+template <typename Type, typename LenType>
struct HeadlessArrayOf
{
static constexpr unsigned item_size = Type::static_size;
@@ -861,6 +884,7 @@
}
template <typename ...Ts>
+ HB_ALWAYS_INLINE
bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const
{
TRACE_SANITIZE (this);
@@ -878,7 +902,7 @@
{
TRACE_SANITIZE (this);
return_trace (lenP1.sanitize (c) &&
- (!lenP1 || c->check_array (arrayZ, lenP1 - 1)));
+ (!lenP1 || c->check_array_sized (arrayZ, lenP1 - 1, sizeof (LenType))));
}
public:
@@ -887,6 +911,7 @@
public:
DEFINE_SIZE_ARRAY (sizeof (LenType), arrayZ);
};
+template <typename Type> using HeadlessArray16Of = HeadlessArrayOf<Type, HBUINT16>;
/* An array storing length-1. */
template <typename Type, typename LenType=HBUINT16>
@@ -912,6 +937,7 @@
{ return lenM1.static_size + (lenM1 + 1) * Type::static_size; }
template <typename ...Ts>
+ HB_ALWAYS_INLINE
bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const
{
TRACE_SANITIZE (this);
@@ -929,7 +955,7 @@
{
TRACE_SANITIZE (this);
return_trace (lenM1.sanitize (c) &&
- (c->check_array (arrayZ, lenM1 + 1)));
+ (c->check_array_sized (arrayZ, lenM1 + 1, sizeof (LenType))));
}
public:
@@ -1096,6 +1122,7 @@
{ return header.static_size + header.nUnits * header.unitSize; }
template <typename ...Ts>
+ HB_ALWAYS_INLINE
bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const
{
TRACE_SANITIZE (this);
diff --git a/src/hb-ot-cff-common.hh b/src/hb-ot-cff-common.hh
index f22824f..923a32b 100644
--- a/src/hb-ot-cff-common.hh
+++ b/src/hb-ot-cff-common.hh
@@ -48,12 +48,24 @@
struct code_pair_t
{
- hb_codepoint_t code;
+ unsigned code;
hb_codepoint_t glyph;
};
+
using str_buff_t = hb_vector_t<unsigned char>;
using str_buff_vec_t = hb_vector_t<str_buff_t>;
+using glyph_to_sid_map_t = hb_vector_t<code_pair_t>;
+
+struct length_f_t
+{
+ template <typename Iterable,
+ hb_requires (hb_is_iterable (Iterable))>
+ unsigned operator () (const Iterable &_) const { return hb_len (hb_iter (_)); }
+
+ unsigned operator () (unsigned _) const { return _; }
+}
+HB_FUNCOBJ (length_f);
/* CFF INDEX */
template <typename COUNT>
@@ -62,42 +74,52 @@
unsigned int offset_array_size () const
{ return offSize * (count + 1); }
- CFFIndex *copy (hb_serialize_context_t *c) const
- {
- TRACE_SERIALIZE (this);
- unsigned int size = get_size ();
- CFFIndex *out = c->allocate_size<CFFIndex> (size, false);
- if (likely (out))
- hb_memcpy (out, this, size);
- return_trace (out);
- }
-
template <typename Iterable,
hb_requires (hb_is_iterable (Iterable))>
bool serialize (hb_serialize_context_t *c,
- const Iterable &iterable)
+ const Iterable &iterable,
+ const unsigned *p_data_size = nullptr)
{
TRACE_SERIALIZE (this);
+ unsigned data_size;
+ if (p_data_size)
+ data_size = *p_data_size;
+ else
+ total_size (iterable, &data_size);
+
auto it = hb_iter (iterable);
- serialize_header(c, + it | hb_map (hb_iter) | hb_map (hb_len));
+ if (unlikely (!serialize_header (c, +it, data_size))) return_trace (false);
+ unsigned char *ret = c->allocate_size<unsigned char> (data_size, false);
+ if (unlikely (!ret)) return_trace (false);
for (const auto &_ : +it)
- hb_iter (_).copy (c);
+ {
+ unsigned len = _.length;
+ if (!len)
+ continue;
+ if (len <= 1)
+ {
+ *ret++ = *_.arrayZ;
+ continue;
+ }
+ hb_memcpy (ret, _.arrayZ, len);
+ ret += len;
+ }
return_trace (true);
}
template <typename Iterator,
hb_requires (hb_is_iterator (Iterator))>
bool serialize_header (hb_serialize_context_t *c,
- Iterator it)
+ Iterator it,
+ unsigned data_size)
{
TRACE_SERIALIZE (this);
- unsigned total = + it | hb_reduce (hb_add, 0);
- unsigned off_size = (hb_bit_storage (total + 1) + 7) / 8;
+ unsigned off_size = (hb_bit_storage (data_size + 1) + 7) / 8;
/* serialize CFFIndex header */
if (unlikely (!c->extend_min (this))) return_trace (false);
- this->count = it.len ();
+ this->count = hb_len (it);
if (!this->count) return_trace (true);
if (unlikely (!c->extend (this->offSize))) return_trace (false);
this->offSize = off_size;
@@ -106,25 +128,88 @@
/* serialize indices */
unsigned int offset = 1;
- unsigned int i = 0;
- for (unsigned _ : +it)
+ if (HB_OPTIMIZE_SIZE_VAL)
{
- set_offset_at (i++, offset);
- offset += _;
+ unsigned int i = 0;
+ for (const auto &_ : +it)
+ {
+ set_offset_at (i++, offset);
+ offset += length_f (_);
+ }
+ set_offset_at (i, offset);
}
- set_offset_at (i, offset);
+ else
+ switch (off_size)
+ {
+ case 1:
+ {
+ HBUINT8 *p = (HBUINT8 *) offsets;
+ for (const auto &_ : +it)
+ {
+ *p++ = offset;
+ offset += length_f (_);
+ }
+ *p = offset;
+ }
+ break;
+ case 2:
+ {
+ HBUINT16 *p = (HBUINT16 *) offsets;
+ for (const auto &_ : +it)
+ {
+ *p++ = offset;
+ offset += length_f (_);
+ }
+ *p = offset;
+ }
+ break;
+ case 3:
+ {
+ HBUINT24 *p = (HBUINT24 *) offsets;
+ for (const auto &_ : +it)
+ {
+ *p++ = offset;
+ offset += length_f (_);
+ }
+ *p = offset;
+ }
+ break;
+ case 4:
+ {
+ HBUINT32 *p = (HBUINT32 *) offsets;
+ for (const auto &_ : +it)
+ {
+ *p++ = offset;
+ offset += length_f (_);
+ }
+ *p = offset;
+ }
+ break;
+ default:
+ break;
+ }
+ assert (offset == data_size + 1);
return_trace (true);
}
template <typename Iterable,
hb_requires (hb_is_iterable (Iterable))>
- static unsigned total_size (const Iterable &iterable)
+ static unsigned total_size (const Iterable &iterable, unsigned *data_size = nullptr)
{
- auto it = + hb_iter (iterable) | hb_map (hb_iter) | hb_map (hb_len);
- if (!it) return 0;
+ auto it = + hb_iter (iterable);
+ if (!it)
+ {
+ if (data_size) *data_size = 0;
+ return min_size;
+ }
- unsigned total = + it | hb_reduce (hb_add, 0);
+ unsigned total = 0;
+ for (const auto &_ : +it)
+ total += length_f (_);
+
+ if (data_size) *data_size = total;
+
unsigned off_size = (hb_bit_storage (total + 1) + 7) / 8;
return min_size + HBUINT8::static_size + (hb_len (it) + 1) * off_size + total;
@@ -133,13 +218,16 @@
void set_offset_at (unsigned int index, unsigned int offset)
{
assert (index <= count);
- HBUINT8 *p = offsets + offSize * index + offSize;
+
unsigned int size = offSize;
- for (; size; size--)
+ const HBUINT8 *p = offsets;
+ switch (size)
{
- --p;
- *p = offset & 0xFF;
- offset >>= 8;
+ case 1: ((HBUINT8 *) p)[index] = offset; break;
+ case 2: ((HBUINT16 *) p)[index] = offset; break;
+ case 3: ((HBUINT24 *) p)[index] = offset; break;
+ case 4: ((HBUINT32 *) p)[index] = offset; break;
+ default: return;
}
}
@@ -149,37 +237,30 @@
assert (index <= count);
unsigned int size = offSize;
- const HBUINT8 *p = offsets + size * index;
+ const HBUINT8 *p = offsets;
switch (size)
{
- case 1: return * (HBUINT8 *) p;
- case 2: return * (HBUINT16 *) p;
- case 3: return * (HBUINT24 *) p;
- case 4: return * (HBUINT32 *) p;
+ case 1: return ((HBUINT8 *) p)[index];
+ case 2: return ((HBUINT16 *) p)[index];
+ case 3: return ((HBUINT24 *) p)[index];
+ case 4: return ((HBUINT32 *) p)[index];
default: return 0;
}
}
- unsigned int length_at (unsigned int index) const
- {
- unsigned offset0 = offset_at (index);
- unsigned offset1 = offset_at (index + 1);
- if (unlikely (offset1 < offset0 || offset1 > offset_at (count)))
- return 0;
- return offset1 - offset0;
- }
-
const unsigned char *data_base () const
- { return (const unsigned char *) this + min_size + offSize.static_size + offset_array_size (); }
+ { return (const unsigned char *) this + min_size + offSize.static_size - 1 + offset_array_size (); }
public:
hb_ubytes_t operator [] (unsigned int index) const
{
if (unlikely (index >= count)) return hb_ubytes_t ();
_hb_compiler_memory_r_barrier ();
- unsigned length = length_at (index);
- if (unlikely (!length)) return hb_ubytes_t ();
- return hb_ubytes_t (data_base () + offset_at (index) - 1, length);
+ unsigned offset0 = offset_at (index);
+ unsigned offset1 = offset_at (index + 1);
+ if (unlikely (offset1 < offset0 || offset1 > offset_at (count)))
+ return hb_ubytes_t ();
+ return hb_ubytes_t (data_base () + offset0, offset1 - offset0);
}
unsigned int get_size () const
@@ -197,7 +278,7 @@
(count < count + 1u &&
c->check_struct (&offSize) && offSize >= 1 && offSize <= 4 &&
c->check_array (offsets, offSize, count + 1u) &&
- c->check_array ((const HBUINT8*) data_base (), 1, offset_at (count) - 1)))));
+ c->check_array ((const HBUINT8*) data_base (), 1, offset_at (count))))));
}
public:
@@ -211,47 +292,6 @@
DEFINE_SIZE_MIN (COUNT::static_size);
};
-template <typename COUNT, typename TYPE>
-struct CFFIndexOf : CFFIndex<COUNT>
-{
- template <typename DATA, typename PARAM1, typename PARAM2>
- bool serialize (hb_serialize_context_t *c,
- unsigned int offSize_,
- const DATA *dataArray,
- unsigned int dataArrayLen,
- const hb_vector_t<unsigned int> &dataSizeArray,
- const PARAM1 ¶m1,
- const PARAM2 ¶m2)
- {
- TRACE_SERIALIZE (this);
- /* serialize CFFIndex header */
- if (unlikely (!c->extend_min (this))) return_trace (false);
- this->count = dataArrayLen;
- this->offSize = offSize_;
- if (unlikely (!c->allocate_size<HBUINT8> (offSize_ * (dataArrayLen + 1), false)))
- return_trace (false);
-
- /* serialize indices */
- unsigned int offset = 1;
- unsigned int i = 0;
- for (; i < dataArrayLen; i++)
- {
- this->set_offset_at (i, offset);
- offset += dataSizeArray[i];
- }
- this->set_offset_at (i, offset);
-
- /* serialize data */
- for (unsigned int i = 0; i < dataArrayLen; i++)
- {
- TYPE *dest = c->start_embed<TYPE> ();
- if (unlikely (!dest || !dest->serialize (c, dataArray[i], param1, param2)))
- return_trace (false);
- }
- return_trace (true);
- }
-};
-
/* Top Dict, Font Dict, Private Dict */
struct Dict : UnsizedByteStr
{
@@ -327,7 +367,7 @@
};
template <typename COUNT>
-struct FDArray : CFFIndexOf<COUNT, FontDict>
+struct FDArray : CFFIndex<COUNT>
{
template <typename DICTVAL, typename INFO, typename Iterator, typename OP_SERIALIZER>
bool serialize (hb_serialize_context_t *c,
@@ -338,7 +378,11 @@
/* serialize INDEX data */
hb_vector_t<unsigned> sizes;
+ if (it.is_random_access_iterator)
+ sizes.alloc (hb_len (it));
+
c->push ();
+ char *data_base = c->head;
+ it
| hb_map ([&] (const hb_pair_t<const DICTVAL&, const INFO&> &_)
{
@@ -348,10 +392,16 @@
})
| hb_sink (sizes)
;
+ unsigned data_size = c->head - data_base;
c->pop_pack (false);
+ if (unlikely (sizes.in_error ())) return_trace (false);
+
+ /* It just happens that the above is packed right after the header below.
+ * Such a hack. */
+
/* serialize INDEX header */
- return_trace (CFFIndex<COUNT>::serialize_header (c, hb_iter (sizes)));
+ return_trace (CFFIndex<COUNT>::serialize_header (c, hb_iter (sizes), data_size));
}
};
@@ -368,8 +418,11 @@
return_trace (true);
}
- hb_codepoint_t get_fd (hb_codepoint_t glyph) const
- { return (hb_codepoint_t) fds[glyph]; }
+ unsigned get_fd (hb_codepoint_t glyph) const
+ { return fds[glyph]; }
+
+ hb_pair_t<unsigned, hb_codepoint_t> get_fd_range (hb_codepoint_t glyph) const
+ { return {fds[glyph], glyph + 1}; }
unsigned int get_size (unsigned int num_glyphs) const
{ return HBUINT8::static_size * num_glyphs; }
@@ -427,12 +480,20 @@
return +1;
}
- hb_codepoint_t get_fd (hb_codepoint_t glyph) const
+ unsigned get_fd (hb_codepoint_t glyph) const
{
auto *range = hb_bsearch (glyph, &ranges[0], nRanges () - 1, sizeof (ranges[0]), _cmp_range);
return range ? range->fd : ranges[nRanges () - 1].fd;
}
+ hb_pair_t<unsigned, hb_codepoint_t> get_fd_range (hb_codepoint_t glyph) const
+ {
+ auto *range = hb_bsearch (glyph, &ranges[0], nRanges () - 1, sizeof (ranges[0]), _cmp_range);
+ unsigned fd = range ? range->fd : ranges[nRanges () - 1].fd;
+ hb_codepoint_t end = range ? range[1].first : ranges[nRanges () - 1].first;
+ return {fd, end};
+ }
+
GID_TYPE &nRanges () { return ranges.len; }
GID_TYPE nRanges () const { return ranges.len; }
GID_TYPE &sentinel () { return StructAfter<GID_TYPE> (ranges[nRanges () - 1]); }
@@ -469,7 +530,7 @@
}
}
- hb_codepoint_t get_fd (hb_codepoint_t glyph) const
+ unsigned get_fd (hb_codepoint_t glyph) const
{
if (this == &Null (FDSelect)) return 0;
@@ -480,6 +541,18 @@
default:return 0;
}
}
+ /* Returns pair of fd and one after last glyph in range. */
+ hb_pair_t<unsigned, hb_codepoint_t> get_fd_range (hb_codepoint_t glyph) const
+ {
+ if (this == &Null (FDSelect)) return {0, 1};
+
+ switch (format)
+ {
+ case 0: return u.format0.get_fd_range (glyph);
+ case 3: return u.format3.get_fd_range (glyph);
+ default:return {0, 1};
+ }
+ }
bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const
{
diff --git a/src/hb-ot-cff1-table.cc b/src/hb-ot-cff1-table.cc
index 5040c74..66df28a 100644
--- a/src/hb-ot-cff1-table.cc
+++ b/src/hb-ot-cff1-table.cc
@@ -574,11 +574,11 @@
struct get_seac_param_t
{
- get_seac_param_t (const OT::cff1::accelerator_t *_cff) : cff (_cff) {}
+ get_seac_param_t (const OT::cff1::accelerator_subset_t *_cff) : cff (_cff) {}
bool has_seac () const { return base && accent; }
- const OT::cff1::accelerator_t *cff;
+ const OT::cff1::accelerator_subset_t *cff;
hb_codepoint_t base = 0;
hb_codepoint_t accent = 0;
};
@@ -596,7 +596,7 @@
}
};
-bool OT::cff1::accelerator_t::get_seac_components (hb_codepoint_t glyph, hb_codepoint_t *base, hb_codepoint_t *accent) const
+bool OT::cff1::accelerator_subset_t::get_seac_components (hb_codepoint_t glyph, hb_codepoint_t *base, hb_codepoint_t *accent) const
{
if (unlikely (!is_valid () || (glyph >= num_glyphs))) return false;
diff --git a/src/hb-ot-cff1-table.hh b/src/hb-ot-cff1-table.hh
index 4d0a965..1e81dcb 100644
--- a/src/hb-ot-cff1-table.hh
+++ b/src/hb-ot-cff1-table.hh
@@ -28,7 +28,7 @@
#define HB_OT_CFF1_TABLE_HH
#include "hb-ot-cff-common.hh"
-#include "hb-subset-cff1.hh"
+#include "hb-subset-cff-common.hh"
#include "hb-draw.hh"
#include "hb-paint.hh"
@@ -52,7 +52,6 @@
enum CharsetID { ISOAdobeCharset = 0, ExpertCharset = 1, ExpertSubsetCharset = 2 };
typedef CFFIndex<HBUINT16> CFF1Index;
-template <typename Type> struct CFF1IndexOf : CFFIndexOf<HBUINT16, Type> {};
typedef CFFIndex<HBUINT16> CFF1Index;
typedef CFF1Index CFF1CharStrings;
@@ -110,6 +109,7 @@
hb_codepoint_t get_code (hb_codepoint_t glyph) const
{
+ /* TODO: Add cache like get_sid. */
assert (glyph > 0);
glyph--;
for (unsigned int i = 0; i < nRanges (); i++)
@@ -173,11 +173,7 @@
bool serialize (hb_serialize_context_t *c, const Encoding &src)
{
TRACE_SERIALIZE (this);
- unsigned int size = src.get_size ();
- Encoding *dest = c->allocate_size<Encoding> (size);
- if (unlikely (!dest)) return_trace (false);
- hb_memcpy (dest, &src, size);
- return_trace (true);
+ return_trace (c->embed (src));
}
/* serialize a subset Encoding */
@@ -312,26 +308,29 @@
};
/* Charset */
-struct Charset0 {
- bool sanitize (hb_sanitize_context_t *c, unsigned int num_glyphs) const
+struct Charset0
+{
+ bool sanitize (hb_sanitize_context_t *c, unsigned int num_glyphs, unsigned *num_charset_entries) const
{
TRACE_SANITIZE (this);
- return_trace (c->check_struct (this) && sids[num_glyphs - 1].sanitize (c));
+ if (num_charset_entries) *num_charset_entries = num_glyphs;
+ return_trace (sids.sanitize (c, num_glyphs - 1));
}
hb_codepoint_t get_sid (hb_codepoint_t glyph, unsigned num_glyphs) const
{
if (unlikely (glyph >= num_glyphs)) return 0;
- if (glyph == 0)
+ if (unlikely (glyph == 0))
return 0;
else
return sids[glyph - 1];
}
- void collect_glyph_to_sid_map (hb_map_t *mapping, unsigned int num_glyphs) const
+ void collect_glyph_to_sid_map (glyph_to_sid_map_t *mapping, unsigned int num_glyphs) const
{
+ mapping->resize (num_glyphs, false);
for (hb_codepoint_t gid = 1; gid < num_glyphs; gid++)
- mapping->set (gid, sids[gid - 1]);
+ mapping->arrayZ[gid] = {sids[gid - 1], gid};
}
hb_codepoint_t get_glyph (hb_codepoint_t sid, unsigned int num_glyphs) const
@@ -347,13 +346,13 @@
return 0;
}
- unsigned int get_size (unsigned int num_glyphs) const
+ static unsigned int get_size (unsigned int num_glyphs)
{
assert (num_glyphs > 0);
- return HBUINT16::static_size * (num_glyphs - 1);
+ return UnsizedArrayOf<HBUINT16>::get_size (num_glyphs - 1);
}
- HBUINT16 sids[HB_VAR_ARRAY];
+ UnsizedArrayOf<HBUINT16> sids;
DEFINE_SIZE_ARRAY(0, sids);
};
@@ -374,38 +373,62 @@
template <typename TYPE>
struct Charset1_2 {
- bool sanitize (hb_sanitize_context_t *c, unsigned int num_glyphs) const
+ bool sanitize (hb_sanitize_context_t *c, unsigned int num_glyphs, unsigned *num_charset_entries) const
{
TRACE_SANITIZE (this);
if (unlikely (!c->check_struct (this)))
return_trace (false);
num_glyphs--;
- for (unsigned int i = 0; num_glyphs > 0; i++)
+ unsigned i;
+ for (i = 0; num_glyphs > 0; i++)
{
if (unlikely (!ranges[i].sanitize (c) || (num_glyphs < ranges[i].nLeft + 1)))
return_trace (false);
num_glyphs -= (ranges[i].nLeft + 1);
}
+ if (num_charset_entries)
+ *num_charset_entries = i;
return_trace (true);
}
- hb_codepoint_t get_sid (hb_codepoint_t glyph, unsigned num_glyphs) const
+ hb_codepoint_t get_sid (hb_codepoint_t glyph, unsigned num_glyphs,
+ code_pair_t *cache = nullptr) const
{
if (unlikely (glyph >= num_glyphs)) return 0;
- if (glyph == 0) return 0;
- glyph--;
- for (unsigned int i = 0;; i++)
+ unsigned i;
+ hb_codepoint_t start_glyph;
+ if (cache && likely (cache->glyph <= glyph))
{
- if (glyph <= ranges[i].nLeft)
- return (hb_codepoint_t) ranges[i].first + glyph;
- glyph -= (ranges[i].nLeft + 1);
+ i = cache->code;
+ start_glyph = cache->glyph;
+ }
+ else
+ {
+ if (unlikely (glyph == 0)) return 0;
+ i = 0;
+ start_glyph = 1;
+ }
+ glyph -= start_glyph;
+ for (;; i++)
+ {
+ unsigned count = ranges[i].nLeft;
+ if (glyph <= count)
+ {
+ if (cache)
+ *cache = {i, start_glyph};
+ return ranges[i].first + glyph;
+ }
+ count++;
+ start_glyph += count;
+ glyph -= count;
}
return 0;
}
- void collect_glyph_to_sid_map (hb_map_t *mapping, unsigned int num_glyphs) const
+ void collect_glyph_to_sid_map (glyph_to_sid_map_t *mapping, unsigned int num_glyphs) const
{
+ mapping->resize (num_glyphs, false);
hb_codepoint_t gid = 1;
if (gid >= num_glyphs)
return;
@@ -413,8 +436,9 @@
{
hb_codepoint_t sid = ranges[i].first;
unsigned count = ranges[i].nLeft + 1;
+ unsigned last = gid + count;
for (unsigned j = 0; j < count; j++)
- mapping->set (gid++, sid++);
+ mapping->arrayZ[gid++] = {sid++, last - 1};
if (gid >= num_glyphs)
break;
@@ -439,21 +463,26 @@
unsigned int get_size (unsigned int num_glyphs) const
{
- unsigned int size = HBUINT8::static_size;
- int glyph = (int)num_glyphs;
+ int glyph = (int) num_glyphs;
+ unsigned num_ranges = 0;
assert (glyph > 0);
glyph--;
for (unsigned int i = 0; glyph > 0; i++)
{
glyph -= (ranges[i].nLeft + 1);
- size += Charset_Range<TYPE>::static_size;
+ num_ranges++;
}
- return size;
+ return get_size_for_ranges (num_ranges);
}
- Charset_Range<TYPE> ranges[HB_VAR_ARRAY];
+ static unsigned int get_size_for_ranges (unsigned int num_ranges)
+ {
+ return UnsizedArrayOf<Charset_Range<TYPE> >::get_size (num_ranges);
+ }
+
+ UnsizedArrayOf<Charset_Range<TYPE>> ranges;
DEFINE_SIZE_ARRAY (0, ranges);
};
@@ -469,11 +498,7 @@
bool serialize (hb_serialize_context_t *c, const Charset &src, unsigned int num_glyphs)
{
TRACE_SERIALIZE (this);
- unsigned int size = src.get_size (num_glyphs);
- Charset *dest = c->allocate_size<Charset> (size);
- if (unlikely (!dest)) return_trace (false);
- hb_memcpy (dest, &src, size);
- return_trace (true);
+ return_trace (c->embed ((const char *) &src, src.get_size (num_glyphs)));
}
/* serialize a subset Charset */
@@ -490,13 +515,13 @@
{
case 0:
{
- Charset0 *fmt0 = c->allocate_size<Charset0> (Charset0::min_size + HBUINT16::static_size * (num_glyphs - 1));
+ Charset0 *fmt0 = c->allocate_size<Charset0> (Charset0::get_size (num_glyphs), false);
if (unlikely (!fmt0)) return_trace (false);
unsigned int glyph = 0;
for (unsigned int i = 0; i < sid_ranges.length; i++)
{
- hb_codepoint_t sid = sid_ranges[i].code;
- for (int left = (int)sid_ranges[i].glyph; left >= 0; left--)
+ hb_codepoint_t sid = sid_ranges.arrayZ[i].code;
+ for (int left = (int)sid_ranges.arrayZ[i].glyph; left >= 0; left--)
fmt0->sids[glyph++] = sid++;
}
}
@@ -504,29 +529,35 @@
case 1:
{
- Charset1 *fmt1 = c->allocate_size<Charset1> (Charset1::min_size + Charset1_Range::static_size * sid_ranges.length);
+ Charset1 *fmt1 = c->allocate_size<Charset1> (Charset1::get_size_for_ranges (sid_ranges.length), false);
if (unlikely (!fmt1)) return_trace (false);
+ hb_codepoint_t all_glyphs = 0;
for (unsigned int i = 0; i < sid_ranges.length; i++)
{
- if (unlikely (!(sid_ranges[i].glyph <= 0xFF)))
- return_trace (false);
- fmt1->ranges[i].first = sid_ranges[i].code;
- fmt1->ranges[i].nLeft = sid_ranges[i].glyph;
+ auto &_ = sid_ranges.arrayZ[i];
+ all_glyphs |= _.glyph;
+ fmt1->ranges[i].first = _.code;
+ fmt1->ranges[i].nLeft = _.glyph;
}
+ if (unlikely (!(all_glyphs <= 0xFF)))
+ return_trace (false);
}
break;
case 2:
{
- Charset2 *fmt2 = c->allocate_size<Charset2> (Charset2::min_size + Charset2_Range::static_size * sid_ranges.length);
+ Charset2 *fmt2 = c->allocate_size<Charset2> (Charset2::get_size_for_ranges (sid_ranges.length), false);
if (unlikely (!fmt2)) return_trace (false);
+ hb_codepoint_t all_glyphs = 0;
for (unsigned int i = 0; i < sid_ranges.length; i++)
{
- if (unlikely (!(sid_ranges[i].glyph <= 0xFFFF)))
- return_trace (false);
- fmt2->ranges[i].first = sid_ranges[i].code;
- fmt2->ranges[i].nLeft = sid_ranges[i].glyph;
+ auto &_ = sid_ranges.arrayZ[i];
+ all_glyphs |= _.glyph;
+ fmt2->ranges[i].first = _.code;
+ fmt2->ranges[i].nLeft = _.glyph;
}
+ if (unlikely (!(all_glyphs <= 0xFFFF)))
+ return_trace (false);
}
break;
@@ -545,18 +576,19 @@
}
}
- hb_codepoint_t get_sid (hb_codepoint_t glyph, unsigned int num_glyphs) const
+ hb_codepoint_t get_sid (hb_codepoint_t glyph, unsigned int num_glyphs,
+ code_pair_t *cache = nullptr) const
{
switch (format)
{
case 0: return u.format0.get_sid (glyph, num_glyphs);
- case 1: return u.format1.get_sid (glyph, num_glyphs);
- case 2: return u.format2.get_sid (glyph, num_glyphs);
+ case 1: return u.format1.get_sid (glyph, num_glyphs, cache);
+ case 2: return u.format2.get_sid (glyph, num_glyphs, cache);
default:return 0;
}
}
- void collect_glyph_to_sid_map (hb_map_t *mapping, unsigned int num_glyphs) const
+ void collect_glyph_to_sid_map (glyph_to_sid_map_t *mapping, unsigned int num_glyphs) const
{
switch (format)
{
@@ -578,7 +610,7 @@
}
}
- bool sanitize (hb_sanitize_context_t *c) const
+ bool sanitize (hb_sanitize_context_t *c, unsigned *num_charset_entries) const
{
TRACE_SANITIZE (this);
if (unlikely (!c->check_struct (this)))
@@ -586,9 +618,9 @@
switch (format)
{
- case 0: return_trace (u.format0.sanitize (c, c->get_num_glyphs ()));
- case 1: return_trace (u.format1.sanitize (c, c->get_num_glyphs ()));
- case 2: return_trace (u.format2.sanitize (c, c->get_num_glyphs ()));
+ case 0: return_trace (u.format0.sanitize (c, c->get_num_glyphs (), num_charset_entries));
+ case 1: return_trace (u.format1.sanitize (c, c->get_num_glyphs (), num_charset_entries));
+ case 2: return_trace (u.format2.sanitize (c, c->get_num_glyphs (), num_charset_entries));
default:return_trace (false);
}
}
@@ -606,10 +638,10 @@
struct CFF1StringIndex : CFF1Index
{
bool serialize (hb_serialize_context_t *c, const CFF1StringIndex &strings,
- const hb_inc_bimap_t &sidmap)
+ const hb_vector_t<unsigned> &sidmap)
{
TRACE_SERIALIZE (this);
- if (unlikely ((strings.count == 0) || (sidmap.get_population () == 0)))
+ if (unlikely ((strings.count == 0) || (sidmap.length == 0)))
{
if (unlikely (!c->extend_min (this->count)))
return_trace (false);
@@ -617,15 +649,13 @@
return_trace (true);
}
- byte_str_array_t bytesArray;
- if (!bytesArray.resize (sidmap.get_population ()))
- return_trace (false);
- for (unsigned int i = 0; i < strings.count; i++)
- {
- hb_codepoint_t j = sidmap[i];
- if (j != HB_MAP_VALUE_INVALID)
- bytesArray[j] = strings[i];
- }
+ if (unlikely (sidmap.in_error ())) return_trace (false);
+
+ // Save this in a vector since serialize() iterates it twice.
+ hb_vector_t<hb_ubytes_t> bytesArray (+ hb_iter (sidmap)
+ | hb_map (strings));
+
+ if (unlikely (bytesArray.in_error ())) return_trace (false);
bool result = CFF1Index::serialize (c, bytesArray);
return_trace (result);
@@ -932,7 +962,7 @@
}
};
-struct cff1_private_dict_opset_subset : dict_opset_t
+struct cff1_private_dict_opset_subset_t : dict_opset_t
{
static void process_op (op_code_t op, num_interp_env_t& env, cff1_private_dict_values_subset_t& dictval)
{
@@ -978,7 +1008,7 @@
typedef dict_interpreter_t<cff1_font_dict_opset_t, cff1_font_dict_values_t> cff1_font_dict_interpreter_t;
typedef CFF1Index CFF1NameIndex;
-typedef CFF1IndexOf<TopDict> CFF1TopDictIndex;
+typedef CFF1Index CFF1TopDictIndex;
struct cff1_font_dict_values_mod_t
{
@@ -1031,8 +1061,12 @@
template <typename PRIVOPSET, typename PRIVDICTVAL>
struct accelerator_templ_t
{
- void init (hb_face_t *face)
+ static constexpr hb_tag_t tableTag = cff1::tableTag;
+
+ accelerator_templ_t (hb_face_t *face)
{
+ if (!face) return;
+
topDict.init ();
fontDicts.init ();
privateDicts.init ();
@@ -1046,22 +1080,22 @@
const OT::cff1 *cff = this->blob->template as<OT::cff1> ();
if (cff == &Null (OT::cff1))
- { fini (); return; }
+ goto fail;
nameIndex = &cff->nameIndex (cff);
if ((nameIndex == &Null (CFF1NameIndex)) || !nameIndex->sanitize (&sc))
- { fini (); return; }
+ goto fail;
topDictIndex = &StructAtOffset<CFF1TopDictIndex> (nameIndex, nameIndex->get_size ());
if ((topDictIndex == &Null (CFF1TopDictIndex)) || !topDictIndex->sanitize (&sc) || (topDictIndex->count == 0))
- { fini (); return; }
+ goto fail;
{ /* parse top dict */
const hb_ubytes_t topDictStr = (*topDictIndex)[0];
- if (unlikely (!topDictStr.sanitize (&sc))) { fini (); return; }
+ if (unlikely (!topDictStr.sanitize (&sc))) goto fail;
cff1_top_dict_interp_env_t env (topDictStr);
cff1_top_dict_interpreter_t top_interp (env);
- if (unlikely (!top_interp.interpret (topDict))) { fini (); return; }
+ if (unlikely (!top_interp.interpret (topDict))) goto fail;
}
if (is_predef_charset ())
@@ -1069,7 +1103,7 @@
else
{
charset = &StructAtOffsetOrNull<Charset> (cff, topDict.CharsetOffset);
- if (unlikely ((charset == &Null (Charset)) || !charset->sanitize (&sc))) { fini (); return; }
+ if (unlikely ((charset == &Null (Charset)) || !charset->sanitize (&sc, &num_charset_entries))) goto fail;
}
fdCount = 1;
@@ -1079,7 +1113,7 @@
fdSelect = &StructAtOffsetOrNull<CFF1FDSelect> (cff, topDict.FDSelectOffset);
if (unlikely ((fdArray == &Null (CFF1FDArray)) || !fdArray->sanitize (&sc) ||
(fdSelect == &Null (CFF1FDSelect)) || !fdSelect->sanitize (&sc, fdArray->count)))
- { fini (); return; }
+ goto fail;
fdCount = fdArray->count;
}
@@ -1092,36 +1126,36 @@
encoding = &Null (Encoding);
if (is_CID ())
{
- if (unlikely (charset == &Null (Charset))) { fini (); return; }
+ if (unlikely (charset == &Null (Charset))) goto fail;
}
else
{
if (!is_predef_encoding ())
{
encoding = &StructAtOffsetOrNull<Encoding> (cff, topDict.EncodingOffset);
- if (unlikely ((encoding == &Null (Encoding)) || !encoding->sanitize (&sc))) { fini (); return; }
+ if (unlikely ((encoding == &Null (Encoding)) || !encoding->sanitize (&sc))) goto fail;
}
}
stringIndex = &StructAtOffset<CFF1StringIndex> (topDictIndex, topDictIndex->get_size ());
if ((stringIndex == &Null (CFF1StringIndex)) || !stringIndex->sanitize (&sc))
- { fini (); return; }
+ goto fail;
globalSubrs = &StructAtOffset<CFF1Subrs> (stringIndex, stringIndex->get_size ());
if ((globalSubrs != &Null (CFF1Subrs)) && !globalSubrs->sanitize (&sc))
- { fini (); return; }
+ goto fail;
charStrings = &StructAtOffsetOrNull<CFF1CharStrings> (cff, topDict.charStringsOffset);
if ((charStrings == &Null (CFF1CharStrings)) || unlikely (!charStrings->sanitize (&sc)))
- { fini (); return; }
+ goto fail;
num_glyphs = charStrings->count;
if (num_glyphs != sc.get_num_glyphs ())
- { fini (); return; }
+ goto fail;
if (unlikely (!privateDicts.resize (fdCount)))
- { fini (); return; }
+ goto fail;
for (unsigned int i = 0; i < fdCount; i++)
privateDicts[i].init ();
@@ -1131,27 +1165,27 @@
for (unsigned int i = 0; i < fdCount; i++)
{
hb_ubytes_t fontDictStr = (*fdArray)[i];
- if (unlikely (!fontDictStr.sanitize (&sc))) { fini (); return; }
+ if (unlikely (!fontDictStr.sanitize (&sc))) goto fail;
cff1_font_dict_values_t *font;
cff1_top_dict_interp_env_t env (fontDictStr);
cff1_font_dict_interpreter_t font_interp (env);
font = fontDicts.push ();
- if (unlikely (fontDicts.in_error ())) { fini (); return; }
+ if (unlikely (fontDicts.in_error ())) goto fail;
font->init ();
- if (unlikely (!font_interp.interpret (*font))) { fini (); return; }
+ if (unlikely (!font_interp.interpret (*font))) goto fail;
PRIVDICTVAL *priv = &privateDicts[i];
const hb_ubytes_t privDictStr = StructAtOffset<UnsizedByteStr> (cff, font->privateDictInfo.offset).as_ubytes (font->privateDictInfo.size);
- if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; }
+ if (unlikely (!privDictStr.sanitize (&sc))) goto fail;
num_interp_env_t env2 (privDictStr);
dict_interpreter_t<PRIVOPSET, PRIVDICTVAL> priv_interp (env2);
priv->init ();
- if (unlikely (!priv_interp.interpret (*priv))) { fini (); return; }
+ if (unlikely (!priv_interp.interpret (*priv))) goto fail;
priv->localSubrs = &StructAtOffsetOrNull<CFF1Subrs> (&privDictStr, priv->subrsOffset);
if (priv->localSubrs != &Null (CFF1Subrs) &&
unlikely (!priv->localSubrs->sanitize (&sc)))
- { fini (); return; }
+ goto fail;
}
}
else /* non-CID */
@@ -1160,20 +1194,25 @@
PRIVDICTVAL *priv = &privateDicts[0];
const hb_ubytes_t privDictStr = StructAtOffset<UnsizedByteStr> (cff, font->privateDictInfo.offset).as_ubytes (font->privateDictInfo.size);
- if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; }
+ if (unlikely (!privDictStr.sanitize (&sc))) goto fail;
num_interp_env_t env (privDictStr);
dict_interpreter_t<PRIVOPSET, PRIVDICTVAL> priv_interp (env);
priv->init ();
- if (unlikely (!priv_interp.interpret (*priv))) { fini (); return; }
+ if (unlikely (!priv_interp.interpret (*priv))) goto fail;
priv->localSubrs = &StructAtOffsetOrNull<CFF1Subrs> (&privDictStr, priv->subrsOffset);
if (priv->localSubrs != &Null (CFF1Subrs) &&
unlikely (!priv->localSubrs->sanitize (&sc)))
- { fini (); return; }
+ goto fail;
}
- }
- void fini ()
+ return;
+
+ fail:
+ _fini ();
+ }
+ ~accelerator_templ_t () { _fini (); }
+ void _fini ()
{
sc.end_processing ();
topDict.fini ();
@@ -1183,6 +1222,8 @@
blob = nullptr;
}
+ hb_blob_t *get_blob () const { return blob; }
+
bool is_valid () const { return blob; }
bool is_CID () const { return topDict.is_CID (); }
@@ -1203,13 +1244,14 @@
bool is_predef_encoding () const { return topDict.EncodingOffset <= ExpertEncoding; }
- hb_codepoint_t glyph_to_code (hb_codepoint_t glyph) const
+ hb_codepoint_t glyph_to_code (hb_codepoint_t glyph,
+ code_pair_t *glyph_to_sid_cache = nullptr) const
{
if (encoding != &Null (Encoding))
return encoding->get_code (glyph);
else
{
- hb_codepoint_t sid = glyph_to_sid (glyph);
+ hb_codepoint_t sid = glyph_to_sid (glyph, glyph_to_sid_cache);
if (sid == 0) return 0;
hb_codepoint_t code = 0;
switch (topDict.EncodingOffset)
@@ -1227,12 +1269,14 @@
}
}
- hb_map_t *create_glyph_to_sid_map () const
+ glyph_to_sid_map_t *create_glyph_to_sid_map () const
{
if (charset != &Null (Charset))
{
- hb_map_t *mapping = hb_map_create ();
- mapping->set (0, 0);
+ auto *mapping = (glyph_to_sid_map_t *) hb_malloc (sizeof (glyph_to_sid_map_t));
+ if (unlikely (!mapping)) return nullptr;
+ mapping = new (mapping) glyph_to_sid_map_t ();
+ mapping->push (code_pair_t {0, 1});
charset->collect_glyph_to_sid_map (mapping, num_glyphs);
return mapping;
}
@@ -1240,10 +1284,11 @@
return nullptr;
}
- hb_codepoint_t glyph_to_sid (hb_codepoint_t glyph) const
+ hb_codepoint_t glyph_to_sid (hb_codepoint_t glyph,
+ code_pair_t *cache = nullptr) const
{
if (charset != &Null (Charset))
- return charset->get_sid (glyph, num_glyphs);
+ return charset->get_sid (glyph, num_glyphs, cache);
else
{
hb_codepoint_t sid = 0;
@@ -1312,19 +1357,17 @@
hb_vector_t<PRIVDICTVAL> privateDicts;
unsigned int num_glyphs = 0;
+ unsigned int num_charset_entries = 0;
};
struct accelerator_t : accelerator_templ_t<cff1_private_dict_opset_t, cff1_private_dict_values_t>
{
- accelerator_t (hb_face_t *face)
+ accelerator_t (hb_face_t *face) : SUPER (face)
{
- SUPER::init (face);
-
glyph_names.set_relaxed (nullptr);
if (!is_valid ()) return;
if (is_CID ()) return;
-
}
~accelerator_t ()
{
@@ -1334,8 +1377,6 @@
names->fini ();
hb_free (names);
}
-
- SUPER::fini ();
}
bool get_glyph_name (hb_codepoint_t glyph,
@@ -1386,9 +1427,10 @@
/* TODO */
/* fill glyph names */
+ code_pair_t glyph_to_sid_cache {0, HB_CODEPOINT_INVALID};
for (hb_codepoint_t gid = 0; gid < num_glyphs; gid++)
{
- hb_codepoint_t sid = glyph_to_sid (gid);
+ hb_codepoint_t sid = glyph_to_sid (gid, &glyph_to_sid_cache);
gname_t gname;
gname.sid = sid;
if (sid < cff1_std_strings_length)
@@ -1426,7 +1468,6 @@
HB_INTERNAL bool get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const;
HB_INTERNAL bool paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const;
- HB_INTERNAL bool get_seac_components (hb_codepoint_t glyph, hb_codepoint_t *base, hb_codepoint_t *accent) const;
HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const;
private:
@@ -1453,9 +1494,24 @@
typedef accelerator_templ_t<cff1_private_dict_opset_t, cff1_private_dict_values_t> SUPER;
};
- struct accelerator_subset_t : accelerator_templ_t<cff1_private_dict_opset_subset, cff1_private_dict_values_subset_t> {};
+ struct accelerator_subset_t : accelerator_templ_t<cff1_private_dict_opset_subset_t, cff1_private_dict_values_subset_t>
+ {
+ accelerator_subset_t (hb_face_t *face) : SUPER (face) {}
+ ~accelerator_subset_t ()
+ {
+ if (cff_accelerator)
+ cff_subset_accelerator_t::destroy (cff_accelerator);
+ }
- bool subset (hb_subset_context_t *c) const { return hb_subset_cff1 (c); }
+ HB_INTERNAL bool subset (hb_subset_context_t *c) const;
+ HB_INTERNAL bool serialize (hb_serialize_context_t *c,
+ struct cff1_subset_plan &plan) const;
+ HB_INTERNAL bool get_seac_components (hb_codepoint_t glyph, hb_codepoint_t *base, hb_codepoint_t *accent) const;
+
+ mutable CFF::cff_subset_accelerator_t* cff_accelerator = nullptr;
+
+ typedef accelerator_templ_t<cff1_private_dict_opset_subset_t, cff1_private_dict_values_subset_t> SUPER;
+ };
protected:
HB_INTERNAL static hb_codepoint_t lookup_standard_encoding_for_code (hb_codepoint_t sid);
@@ -1479,6 +1535,10 @@
cff1_accelerator_t (hb_face_t *face) : cff1::accelerator_t (face) {}
};
+struct cff1_subset_accelerator_t : cff1::accelerator_subset_t {
+ cff1_subset_accelerator_t (hb_face_t *face) : cff1::accelerator_subset_t (face) {}
+};
+
} /* namespace OT */
#endif /* HB_OT_CFF1_TABLE_HH */
diff --git a/src/hb-ot-cff2-table.hh b/src/hb-ot-cff2-table.hh
index 2134d48..af24bb9 100644
--- a/src/hb-ot-cff2-table.hh
+++ b/src/hb-ot-cff2-table.hh
@@ -28,7 +28,7 @@
#define HB_OT_CFF2_TABLE_HH
#include "hb-ot-cff-common.hh"
-#include "hb-subset-cff2.hh"
+#include "hb-subset-cff-common.hh"
#include "hb-draw.hh"
#include "hb-paint.hh"
@@ -41,7 +41,6 @@
#define HB_OT_TAG_CFF2 HB_TAG('C','F','F','2')
typedef CFFIndex<HBUINT32> CFF2Index;
-template <typename Type> struct CFF2IndexOf : CFFIndexOf<HBUINT32, Type> {};
typedef CFF2Index CFF2CharStrings;
typedef Subrs<HBUINT32> CFF2Subrs;
@@ -391,8 +390,12 @@
template <typename PRIVOPSET, typename PRIVDICTVAL>
struct accelerator_templ_t
{
+ static constexpr hb_tag_t tableTag = cff2::tableTag;
+
accelerator_templ_t (hb_face_t *face)
{
+ if (!face) return;
+
topDict.init ();
fontDicts.init ();
privateDicts.init ();
@@ -464,7 +467,6 @@
goto fail;
}
-
return;
fail:
@@ -481,11 +483,13 @@
blob = nullptr;
}
- hb_map_t *create_glyph_to_sid_map () const
+ hb_vector_t<uint16_t> *create_glyph_to_sid_map () const
{
return nullptr;
}
+ hb_blob_t *get_blob () const { return blob; }
+
bool is_valid () const { return blob; }
protected:
@@ -518,9 +522,24 @@
HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const;
};
- typedef accelerator_templ_t<cff2_private_dict_opset_subset_t, cff2_private_dict_values_subset_t> accelerator_subset_t;
+ struct accelerator_subset_t : accelerator_templ_t<cff2_private_dict_opset_subset_t, cff2_private_dict_values_subset_t>
+ {
+ accelerator_subset_t (hb_face_t *face) : SUPER (face) {}
+ ~accelerator_subset_t ()
+ {
+ if (cff_accelerator)
+ cff_subset_accelerator_t::destroy (cff_accelerator);
+ }
- bool subset (hb_subset_context_t *c) const { return hb_subset_cff2 (c); }
+ HB_INTERNAL bool subset (hb_subset_context_t *c) const;
+ HB_INTERNAL bool serialize (hb_serialize_context_t *c,
+ struct cff2_subset_plan &plan,
+ hb_array_t<int> normalized_coords) const;
+
+ mutable CFF::cff_subset_accelerator_t* cff_accelerator = nullptr;
+
+ typedef accelerator_templ_t<cff2_private_dict_opset_subset_t, cff2_private_dict_values_subset_t> SUPER;
+ };
public:
FixedVersion<HBUINT8> version; /* Version of CFF2 table. set to 0x0200u */
@@ -535,6 +554,10 @@
cff2_accelerator_t (hb_face_t *face) : cff2::accelerator_t (face) {}
};
+struct cff2_subset_accelerator_t : cff2::accelerator_subset_t {
+ cff2_subset_accelerator_t (hb_face_t *face) : cff2::accelerator_subset_t (face) {}
+};
+
} /* namespace OT */
#endif /* HB_OT_CFF2_TABLE_HH */
diff --git a/src/hb-ot-cmap-table.hh b/src/hb-ot-cmap-table.hh
index cf5ccd5..30401b1 100644
--- a/src/hb-ot-cmap-table.hh
+++ b/src/hb-ot-cmap-table.hh
@@ -277,10 +277,10 @@
}
} writer(c);
- writer.end_code_ = c->allocate_size<HBUINT16> (HBUINT16::static_size * segcount);
- c->allocate_size<HBUINT16> (2); // padding
- writer.start_code_ = c->allocate_size<HBUINT16> (HBUINT16::static_size * segcount);
- writer.id_delta_ = c->allocate_size<HBINT16> (HBINT16::static_size * segcount);
+ writer.end_code_ = c->allocate_size<HBUINT16> (HBUINT16::static_size * segcount, false);
+ (void) c->allocate_size<HBUINT16> (2); // padding
+ writer.start_code_ = c->allocate_size<HBUINT16> (HBUINT16::static_size * segcount, false);
+ writer.id_delta_ = c->allocate_size<HBINT16> (HBINT16::static_size * segcount, false);
if (unlikely (!writer.end_code_ || !writer.start_code_ || !writer.id_delta_)) return false;
@@ -325,7 +325,7 @@
{
auto format4_iter =
+ it
- | hb_filter ([&] (const hb_pair_t<hb_codepoint_t, hb_codepoint_t> _)
+ | hb_filter ([&] (const hb_codepoint_pair_t _)
{ return _.first <= 0xFFFF; })
;
@@ -335,7 +335,7 @@
if (unlikely (!c->extend_min (this))) return;
this->format = 4;
- hb_vector_t<hb_pair_t<hb_codepoint_t, hb_codepoint_t>> cp_to_gid {
+ hb_vector_t<hb_codepoint_pair_t> cp_to_gid {
format4_iter
};
@@ -757,8 +757,7 @@
hb_codepoint_t gid = this->groups[i].glyphID;
if (!gid)
{
- /* Intention is: if (hb_is_same (T, CmapSubtableFormat13)) continue; */
- if (! T::group_get_glyph (this->groups[i], end)) continue;
+ if (T::formatNumber == 13) continue;
start++;
gid++;
}
@@ -766,11 +765,13 @@
if (unlikely ((unsigned int) (gid + end - start) >= num_glyphs))
end = start + (hb_codepoint_t) num_glyphs - gid;
+ mapping->alloc (mapping->get_population () + end - start + 1);
+
for (unsigned cp = start; cp <= end; cp++)
{
unicodes->add (cp);
mapping->set (cp, gid);
- gid++;
+ gid += T::increment;
}
}
}
@@ -794,6 +795,9 @@
struct CmapSubtableFormat12 : CmapSubtableLongSegmented<CmapSubtableFormat12>
{
+ static constexpr int increment = 1;
+ static constexpr int formatNumber = 12;
+
static hb_codepoint_t group_get_glyph (const CmapSubtableLongGroup &group,
hb_codepoint_t u)
{ return likely (group.startCharCode <= group.endCharCode) ?
@@ -866,6 +870,9 @@
struct CmapSubtableFormat13 : CmapSubtableLongSegmented<CmapSubtableFormat13>
{
+ static constexpr int increment = 0;
+ static constexpr int formatNumber = 13;
+
static hb_codepoint_t group_get_glyph (const CmapSubtableLongGroup &group,
hb_codepoint_t u HB_UNUSED)
{ return group.glyphID; }
@@ -917,8 +924,7 @@
DefaultUVS* copy (hb_serialize_context_t *c,
const hb_set_t *unicodes) const
{
- DefaultUVS *out = c->start_embed<DefaultUVS> ();
- if (unlikely (!out)) return nullptr;
+ auto *out = c->start_embed<DefaultUVS> ();
auto snap = c->snapshot ();
HBUINT32 len;
@@ -931,8 +937,7 @@
hb_codepoint_t start = HB_SET_VALUE_INVALID;
hb_codepoint_t end = HB_SET_VALUE_INVALID;
- for (hb_codepoint_t u = HB_SET_VALUE_INVALID;
- unicodes->next (&u);)
+ for (auto u : *unicodes)
{
if (!as_array ().bsearch (u))
continue;
@@ -1067,9 +1072,7 @@
const hb_set_t *glyphs_requested,
const hb_map_t *glyph_map) const
{
- NonDefaultUVS *out = c->start_embed<NonDefaultUVS> ();
- if (unlikely (!out)) return nullptr;
-
+ auto *out = c->start_embed<NonDefaultUVS> ();
auto it =
+ as_array ()
| hb_filter ([&] (const UVSMapping& _)
@@ -1767,7 +1770,6 @@
TRACE_SUBSET (this);
cmap *cmap_prime = c->serializer->start_embed<cmap> ();
- if (unlikely (!c->serializer->check_success (cmap_prime))) return_trace (false);
auto encodingrec_iter =
+ hb_iter (encodingRecord)
@@ -1798,7 +1800,7 @@
auto it =
+ c->plan->unicode_to_new_gid_list.iter ()
- | hb_filter ([&] (const hb_pair_t<hb_codepoint_t, hb_codepoint_t> _)
+ | hb_filter ([&] (const hb_codepoint_pair_t _)
{ return (_.second != HB_MAP_VALUE_INVALID); })
;
diff --git a/src/hb-ot-font.cc b/src/hb-ot-font.cc
index c89a195..b3677c6 100644
--- a/src/hb-ot-font.cc
+++ b/src/hb-ot-font.cc
@@ -38,8 +38,8 @@
#include "hb-ot-cmap-table.hh"
#include "hb-ot-glyf-table.hh"
-#include "hb-ot-cff1-table.hh"
#include "hb-ot-cff2-table.hh"
+#include "hb-ot-cff1-table.hh"
#include "hb-ot-hmtx-table.hh"
#include "hb-ot-post-table.hh"
#include "hb-ot-stat-table.hh" // Just so we compile it; unused otherwise.
@@ -98,7 +98,7 @@
{
cmap_cache = (hb_ot_font_cmap_cache_t *) hb_malloc (sizeof (hb_ot_font_cmap_cache_t));
if (unlikely (!cmap_cache)) goto out;
- cmap_cache->init ();
+ new (cmap_cache) hb_ot_font_cmap_cache_t ();
if (unlikely (!hb_face_set_user_data (font->face,
&hb_ot_font_cmap_cache_user_data_key,
cmap_cache,
@@ -230,8 +230,8 @@
use_cache = false;
goto out;
}
+ new (cache) hb_ot_font_advance_cache_t;
- cache->init ();
if (unlikely (!ot_font->advance_cache.cmpexch (nullptr, cache)))
{
hb_free (cache);
@@ -255,7 +255,7 @@
{ /* Use cache. */
if (ot_font->cached_coords_serial.get_acquire () != (int) font->serial_coords)
{
- ot_font->advance_cache->init ();
+ ot_font->advance_cache->clear ();
ot_font->cached_coords_serial.set_release (font->serial_coords);
}
@@ -436,8 +436,8 @@
#endif
if (ot_face->glyf->get_extents (font, glyph, extents)) return true;
#ifndef HB_NO_OT_FONT_CFF
- if (ot_face->cff1->get_extents (font, glyph, extents)) return true;
if (ot_face->cff2->get_extents (font, glyph, extents)) return true;
+ if (ot_face->cff1->get_extents (font, glyph, extents)) return true;
#endif
return false;
@@ -525,8 +525,8 @@
embolden ? &outline : draw_data, font->slant_xy);
if (!font->face->table.glyf->get_path (font, glyph, draw_session))
#ifndef HB_NO_CFF
- if (!font->face->table.cff1->get_path (font, glyph, draw_session))
if (!font->face->table.cff2->get_path (font, glyph, draw_session))
+ if (!font->face->table.cff1->get_path (font, glyph, draw_session))
#endif
{}
}
@@ -565,8 +565,8 @@
#endif
if (font->face->table.glyf->paint_glyph (font, glyph, paint_funcs, paint_data, foreground)) return;
#ifndef HB_NO_CFF
- if (font->face->table.cff1->paint_glyph (font, glyph, paint_funcs, paint_data, foreground)) return;
if (font->face->table.cff2->paint_glyph (font, glyph, paint_funcs, paint_data, foreground)) return;
+ if (font->face->table.cff1->paint_glyph (font, glyph, paint_funcs, paint_data, foreground)) return;
#endif
}
#endif
diff --git a/src/hb-ot-hdmx-table.hh b/src/hb-ot-hdmx-table.hh
index 3bfd755..cbcf6f5 100644
--- a/src/hb-ot-hdmx-table.hh
+++ b/src/hb-ot-hdmx-table.hh
@@ -46,21 +46,23 @@
template<typename Iterator,
hb_requires (hb_is_iterator (Iterator))>
- bool serialize (hb_serialize_context_t *c, unsigned pixelSize, Iterator it)
+ bool serialize (hb_serialize_context_t *c,
+ unsigned pixelSize,
+ Iterator it,
+ const hb_vector_t<hb_codepoint_pair_t> new_to_old_gid_list,
+ unsigned num_glyphs)
{
TRACE_SERIALIZE (this);
- unsigned length = it.len ();
-
- if (unlikely (!c->extend (this, length))) return_trace (false);
+ if (unlikely (!c->extend (this, num_glyphs))) return_trace (false);
this->pixelSize = pixelSize;
this->maxWidth =
+ it
| hb_reduce (hb_max, 0u);
- + it
- | hb_sink (widthsZ.as_array (length));
+ for (auto &_ : new_to_old_gid_list)
+ widthsZ[_.first] = *it++;
return_trace (true);
}
@@ -89,7 +91,11 @@
template<typename Iterator,
hb_requires (hb_is_iterator (Iterator))>
- bool serialize (hb_serialize_context_t *c, unsigned version, Iterator it)
+ bool serialize (hb_serialize_context_t *c,
+ unsigned version,
+ Iterator it,
+ const hb_vector_t<hb_codepoint_pair_t> &new_to_old_gid_list,
+ unsigned num_glyphs)
{
TRACE_SERIALIZE (this);
@@ -97,10 +103,10 @@
this->version = version;
this->numRecords = it.len ();
- this->sizeDeviceRecord = DeviceRecord::get_size (it ? (*it).second.len () : 0);
+ this->sizeDeviceRecord = DeviceRecord::get_size (num_glyphs);
for (const hb_item_type<Iterator>& _ : +it)
- c->start_embed<DeviceRecord> ()->serialize (c, _.first, _.second);
+ c->start_embed<DeviceRecord> ()->serialize (c, _.first, _.second, new_to_old_gid_list, num_glyphs);
return_trace (c->successful ());
}
@@ -110,31 +116,30 @@
{
TRACE_SUBSET (this);
- hdmx *hdmx_prime = c->serializer->start_embed <hdmx> ();
- if (unlikely (!hdmx_prime)) return_trace (false);
+ auto *hdmx_prime = c->serializer->start_embed <hdmx> ();
+ unsigned num_input_glyphs = get_num_glyphs ();
auto it =
+ hb_range ((unsigned) numRecords)
- | hb_map ([c, this] (unsigned _)
+ | hb_map ([c, num_input_glyphs, this] (unsigned _)
{
const DeviceRecord *device_record =
&StructAtOffset<DeviceRecord> (&firstDeviceRecord,
_ * sizeDeviceRecord);
auto row =
- + hb_range (c->plan->num_output_glyphs ())
- | hb_map (c->plan->reverse_glyph_map)
- | hb_map ([this, c, device_record] (hb_codepoint_t _)
+ + hb_iter (c->plan->new_to_old_gid_list)
+ | hb_map ([num_input_glyphs, device_record] (hb_codepoint_pair_t _)
{
- if (c->plan->is_empty_glyph (_))
- return Null (HBUINT8);
- return device_record->widthsZ.as_array (get_num_glyphs ()) [_];
+ return device_record->widthsZ.as_array (num_input_glyphs) [_.second];
})
;
return hb_pair ((unsigned) device_record->pixelSize, +row);
})
;
- hdmx_prime->serialize (c->serializer, version, it);
+ hdmx_prime->serialize (c->serializer, version, it,
+ c->plan->new_to_old_gid_list,
+ c->plan->num_output_glyphs ());
return_trace (true);
}
diff --git a/src/hb-ot-hmtx-table.hh b/src/hb-ot-hmtx-table.hh
index 835a1a5..89640b4 100644
--- a/src/hb-ot-hmtx-table.hh
+++ b/src/hb-ot-hmtx-table.hh
@@ -83,7 +83,7 @@
bool subset_update_header (hb_subset_context_t *c,
unsigned int num_hmetrics,
const hb_hashmap_t<hb_codepoint_t, hb_pair_t<unsigned, int>> *mtx_map,
- const hb_map_t *bounds_map) const
+ const hb_vector_t<unsigned> &bounds_vec) const
{
hb_blob_t *src_blob = hb_sanitize_context_t ().reference_table<H> (c->plan->source, H::tableTag);
hb_blob_t *dest_blob = hb_blob_copy_writable_or_fail (src_blob);
@@ -114,6 +114,7 @@
HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_VERTICAL_CARET_OFFSET, caretOffset);
}
+ bool empty = true;
int min_lsb = 0x7FFF;
int min_rsb = 0x7FFF;
int max_extent = -0x7FFF;
@@ -125,9 +126,10 @@
int lsb = _.second.second;
max_adv = hb_max (max_adv, adv);
- if (bounds_map->has (gid))
+ if (bounds_vec[gid] != 0xFFFFFFFF)
{
- unsigned bound_width = bounds_map->get (gid);
+ empty = false;
+ unsigned bound_width = bounds_vec[gid];
int rsb = adv - lsb - bound_width;
int extent = lsb + bound_width;
min_lsb = hb_min (min_lsb, lsb);
@@ -137,7 +139,7 @@
}
table->advanceMax = max_adv;
- if (!bounds_map->is_empty ())
+ if (!empty)
{
table->minLeadingBearing = min_lsb;
table->minTrailingBearing = min_rsb;
@@ -156,32 +158,32 @@
hb_requires (hb_is_iterator (Iterator))>
void serialize (hb_serialize_context_t *c,
Iterator it,
- unsigned num_long_metrics)
+ const hb_vector_t<hb_codepoint_pair_t> new_to_old_gid_list,
+ unsigned num_long_metrics,
+ unsigned total_num_metrics)
{
- unsigned idx = 0;
- for (auto _ : it)
+ LongMetric* long_metrics = c->allocate_size<LongMetric> (num_long_metrics * LongMetric::static_size);
+ FWORD* short_metrics = c->allocate_size<FWORD> ((total_num_metrics - num_long_metrics) * FWORD::static_size);
+ if (!long_metrics || !short_metrics) return;
+
+ short_metrics -= num_long_metrics;
+
+ for (auto _ : new_to_old_gid_list)
{
- if (idx < num_long_metrics)
+ hb_codepoint_t gid = _.first;
+ auto mtx = *it++;
+
+ if (gid < num_long_metrics)
{
- LongMetric lm;
- lm.advance = _.first;
- lm.sb = _.second;
- if (unlikely (!c->embed<LongMetric> (&lm))) return;
+ LongMetric& lm = long_metrics[gid];
+ lm.advance = mtx.first;
+ lm.sb = mtx.second;
}
- else if (idx < 0x10000u)
- {
- FWORD *sb = c->allocate_size<FWORD> (FWORD::static_size);
- if (unlikely (!sb)) return;
- *sb = _.second;
- }
+ // TODO(beyond-64k): This assumes that maxp.numGlyphs is 0xFFFF.
+ else if (gid < 0x10000u)
+ short_metrics[gid] = mtx.second;
else
- {
- // TODO: This does not do tail optimization.
- UFWORD *adv = c->allocate_size<UFWORD> (UFWORD::static_size);
- if (unlikely (!adv)) return;
- *adv = _.first;
- }
- idx++;
+ ((UFWORD*) short_metrics)[gid] = mtx.first;
}
}
@@ -189,8 +191,7 @@
{
TRACE_SUBSET (this);
- T *table_prime = c->serializer->start_embed <T> ();
- if (unlikely (!table_prime)) return_trace (false);
+ auto *table_prime = c->serializer->start_embed <T> ();
accelerator_t _mtx (c->plan->source);
unsigned num_long_metrics;
@@ -199,6 +200,8 @@
/* Determine num_long_metrics to encode. */
auto& plan = c->plan;
+ // TODO Don't consider retaingid holes here.
+
num_long_metrics = hb_min (plan->num_output_glyphs (), 0xFFFFu);
unsigned int last_advance = get_new_gid_advance_unscaled (plan, mtx_map, num_long_metrics - 1, _mtx);
while (num_long_metrics > 1 &&
@@ -209,31 +212,36 @@
}
auto it =
- + hb_range (c->plan->num_output_glyphs ())
- | hb_map ([c, &_mtx, mtx_map] (unsigned _)
+ + hb_iter (c->plan->new_to_old_gid_list)
+ | hb_map ([c, &_mtx, mtx_map] (hb_codepoint_pair_t _)
{
- if (!mtx_map->has (_))
+ hb_codepoint_t new_gid = _.first;
+ hb_codepoint_t old_gid = _.second;
+
+ hb_pair_t<unsigned, int> *v = nullptr;
+ if (!mtx_map->has (new_gid, &v))
{
- hb_codepoint_t old_gid;
- if (!c->plan->old_gid_for_new_gid (_, &old_gid))
- return hb_pair (0u, 0);
int lsb = 0;
if (!_mtx.get_leading_bearing_without_var_unscaled (old_gid, &lsb))
(void) _glyf_get_leading_bearing_without_var_unscaled (c->plan->source, old_gid, !T::is_horizontal, &lsb);
return hb_pair (_mtx.get_advance_without_var_unscaled (old_gid), +lsb);
}
- return mtx_map->get (_);
+ return *v;
})
;
- table_prime->serialize (c->serializer, it, num_long_metrics);
+ table_prime->serialize (c->serializer,
+ it,
+ c->plan->new_to_old_gid_list,
+ num_long_metrics,
+ c->plan->num_output_glyphs ());
if (unlikely (c->serializer->in_error ()))
return_trace (false);
// Amend header num hmetrics
if (unlikely (!subset_update_header (c, num_long_metrics, mtx_map,
- T::is_horizontal ? &c->plan->bounds_width_map : &c->plan->bounds_height_map)))
+ T::is_horizontal ? c->plan->bounds_width_vec : c->plan->bounds_height_vec)))
return_trace (false);
return_trace (true);
diff --git a/src/hb-ot-layout-base-table.hh b/src/hb-ot-layout-base-table.hh
index 8179e5a..2b7e9e4 100644
--- a/src/hb-ot-layout-base-table.hh
+++ b/src/hb-ot-layout-base-table.hh
@@ -170,8 +170,8 @@
{
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this) &&
- minCoord.sanitize (c, this) &&
- maxCoord.sanitize (c, this)));
+ minCoord.sanitize (c, base) &&
+ maxCoord.sanitize (c, base)));
}
protected:
@@ -187,7 +187,6 @@
* of MinMax table (may be NULL) */
public:
DEFINE_SIZE_STATIC (8);
-
};
struct MinMax
@@ -274,7 +273,7 @@
{
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this) &&
- minMax.sanitize (c, this)));
+ minMax.sanitize (c, base)));
}
protected:
@@ -297,7 +296,8 @@
const BaseCoord &get_base_coord (int baseline_tag_index) const
{ return (this+baseValues).get_base_coord (baseline_tag_index); }
- bool has_data () const { return baseValues; }
+ bool has_values () const { return baseValues; }
+ bool has_min_max () const { return defaultMinMax; /* TODO What if only per-language is present? */ }
bool sanitize (hb_sanitize_context_t *c) const
{
@@ -383,7 +383,7 @@
const BaseCoord **coord) const
{
const BaseScript &base_script = (this+baseScriptList).get_base_script (script_tag);
- if (!base_script.has_data ())
+ if (!base_script.has_values ())
{
*coord = nullptr;
return false;
@@ -410,7 +410,7 @@
const BaseCoord **max_coord) const
{
const BaseScript &base_script = (this+baseScriptList).get_base_script (script_tag);
- if (!base_script.has_data ())
+ if (!base_script.has_min_max ())
{
*min_coord = *max_coord = nullptr;
return false;
@@ -425,8 +425,8 @@
{
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this) &&
- (this+baseTagList).sanitize (c) &&
- (this+baseScriptList).sanitize (c)));
+ baseTagList.sanitize (c, this) &&
+ baseScriptList.sanitize (c, this)));
}
protected:
@@ -473,14 +473,13 @@
return true;
}
- /* TODO: Expose this separately sometime? */
bool get_min_max (hb_font_t *font,
hb_direction_t direction,
hb_tag_t script_tag,
hb_tag_t language_tag,
hb_tag_t feature_tag,
hb_position_t *min,
- hb_position_t *max)
+ hb_position_t *max) const
{
const BaseCoord *min_coord, *max_coord;
if (!get_axis (direction).get_min_max (script_tag, language_tag, feature_tag,
diff --git a/src/hb-ot-layout-common.hh b/src/hb-ot-layout-common.hh
index 36f123b..b3af128 100644
--- a/src/hb-ot-layout-common.hh
+++ b/src/hb-ot-layout-common.hh
@@ -55,19 +55,22 @@
hb_serialize_context_t *c,
const hb_set_t &klasses,
bool use_class_zero,
- hb_sorted_vector_t<hb_pair_t<hb_codepoint_t, hb_codepoint_t>> &glyph_and_klass, /* IN/OUT */
+ hb_sorted_vector_t<hb_codepoint_pair_t> &glyph_and_klass, /* IN/OUT */
hb_map_t *klass_map /*IN/OUT*/);
struct hb_collect_feature_substitutes_with_var_context_t
{
const hb_map_t *axes_index_tag_map;
- const hb_hashmap_t<hb_tag_t, int> *axes_location;
+ const hb_hashmap_t<hb_tag_t, Triple> *axes_location;
hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *record_cond_idx_map;
hb_hashmap_t<unsigned, const Feature*> *feature_substitutes_map;
+ bool& insert_catch_all_feature_variation_record;
// not stored in subset_plan
hb_set_t *feature_indices;
bool apply;
+ bool variation_applied;
+ bool universal;
unsigned cur_record_idx;
hb_hashmap_t<hb::shared_ptr<hb_map_t>, unsigned> *conditionset_map;
};
@@ -807,7 +810,7 @@
{
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (*this);
- if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
+ if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
out->featureParams.serialize_subset (c, featureParams, this, tag);
@@ -981,7 +984,7 @@
{
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (*this);
- if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
+ if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+ hb_enumerate (*this)
| hb_filter (l->feature_index_map, hb_first)
@@ -1078,7 +1081,7 @@
{
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (*this);
- if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
+ if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
const uint32_t *v;
out->reqFeatureIndex = l->feature_index_map->has (reqFeatureIndex, &v) ? *v : 0xFFFFu;
@@ -1188,7 +1191,7 @@
return false;
auto *out = c->serializer->start_embed (*this);
- if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
+ if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
bool defaultLang = false;
if (has_default_lang_sys ())
@@ -1247,7 +1250,7 @@
{
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (*this);
- if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
+ if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
for (auto _ : + hb_enumerate (*this))
{
@@ -1367,7 +1370,7 @@
{
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (*this);
- if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
+ if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
out->lookupType = lookupType;
out->lookupFlag = lookupFlag;
@@ -1456,7 +1459,7 @@
{
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (this);
- if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
+ if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+ hb_enumerate (*this)
| hb_filter (l->lookup_index_map, hb_first)
@@ -1482,7 +1485,7 @@
static bool ClassDef_remap_and_serialize (hb_serialize_context_t *c,
const hb_set_t &klasses,
bool use_class_zero,
- hb_sorted_vector_t<hb_pair_t<hb_codepoint_t, hb_codepoint_t>> &glyph_and_klass, /* IN/OUT */
+ hb_sorted_vector_t<hb_codepoint_pair_t> &glyph_and_klass, /* IN/OUT */
hb_map_t *klass_map /*IN/OUT*/)
{
if (!klass_map)
@@ -1573,7 +1576,7 @@
TRACE_SUBSET (this);
const hb_map_t &glyph_map = c->plan->glyph_map_gsub;
- hb_sorted_vector_t<hb_pair_t<hb_codepoint_t, hb_codepoint_t>> glyph_and_klass;
+ hb_sorted_vector_t<hb_codepoint_pair_t> glyph_and_klass;
hb_set_t orig_klasses;
hb_codepoint_t start = startGlyph;
@@ -1592,10 +1595,13 @@
orig_klasses.add (klass);
}
- unsigned glyph_count = glyph_filter
- ? hb_len (hb_iter (glyph_map.keys()) | hb_filter (glyph_filter))
- : glyph_map.get_population ();
- use_class_zero = use_class_zero && glyph_count <= glyph_and_klass.length;
+ if (use_class_zero)
+ {
+ unsigned glyph_count = glyph_filter
+ ? hb_len (hb_iter (glyph_map.keys()) | hb_filter (glyph_filter))
+ : glyph_map.get_population ();
+ use_class_zero = glyph_count <= glyph_and_klass.length;
+ }
if (!ClassDef_remap_and_serialize (c->serializer,
orig_klasses,
use_class_zero,
@@ -1830,7 +1836,7 @@
const hb_map_t &glyph_map = c->plan->glyph_map_gsub;
const hb_set_t &glyph_set = *c->plan->glyphset_gsub ();
- hb_sorted_vector_t<hb_pair_t<hb_codepoint_t, hb_codepoint_t>> glyph_and_klass;
+ hb_sorted_vector_t<hb_codepoint_pair_t> glyph_and_klass;
hb_set_t orig_klasses;
if (glyph_set.get_population () * hb_bit_storage ((unsigned) rangeRecord.len) / 2
@@ -1916,7 +1922,7 @@
{
if (rangeRecord.len > glyphs->get_population () * hb_bit_storage ((unsigned) rangeRecord.len) / 2)
{
- for (hb_codepoint_t g = HB_SET_VALUE_INVALID; glyphs->next (&g);)
+ for (auto g : *glyphs)
if (get_class (g))
return true;
return false;
@@ -1976,8 +1982,7 @@
unsigned count = rangeRecord.len;
if (count > glyphs->get_population () * hb_bit_storage (count) * 8)
{
- for (hb_codepoint_t g = HB_SET_VALUE_INVALID;
- glyphs->next (&g);)
+ for (auto g : *glyphs)
{
unsigned i;
if (rangeRecord.as_array ().bfind (g, &i) &&
@@ -2377,7 +2382,7 @@
return_trace (c->check_struct (this) && axesZ.sanitize (c, axisCount * regionCount));
}
- bool serialize (hb_serialize_context_t *c, const VarRegionList *src, const hb_bimap_t ®ion_map)
+ bool serialize (hb_serialize_context_t *c, const VarRegionList *src, const hb_inc_bimap_t ®ion_map)
{
TRACE_SERIALIZE (this);
if (unlikely (!c->extend_min (this))) return_trace (false);
@@ -2494,7 +2499,7 @@
bool serialize (hb_serialize_context_t *c,
const VarData *src,
const hb_inc_bimap_t &inner_map,
- const hb_bimap_t ®ion_map)
+ const hb_inc_bimap_t ®ion_map)
{
TRACE_SERIALIZE (this);
if (unlikely (!c->extend_min (this))) return_trace (false);
@@ -2905,9 +2910,9 @@
enum Cond_with_Var_flag_t
{
KEEP_COND_WITH_VAR = 0,
- DROP_COND_WITH_VAR = 1,
- DROP_RECORD_WITH_VAR = 2,
- MEM_ERR_WITH_VAR = 3,
+ KEEP_RECORD_WITH_VAR = 1,
+ DROP_COND_WITH_VAR = 2,
+ DROP_RECORD_WITH_VAR = 3,
};
struct ConditionFormat1
@@ -2940,29 +2945,42 @@
hb_tag_t axis_tag = c->axes_index_tag_map->get (axisIndex);
- //axis not pinned, keep the condition
- if (!c->axes_location->has (axis_tag))
+ Triple axis_range (-1.f, 0.f, 1.f);
+ if (c->axes_location->has (axis_tag))
+ axis_range = c->axes_location->get (axis_tag);
+
+ int axis_min_val = axis_range.minimum;
+ int axis_default_val = axis_range.middle;
+ int axis_max_val = axis_range.maximum;
+
+ int16_t filter_min_val = filterRangeMinValue.to_int ();
+ int16_t filter_max_val = filterRangeMaxValue.to_int ();
+
+ if (axis_default_val < filter_min_val ||
+ axis_default_val > filter_max_val)
+ c->apply = false;
+
+ //condition not met, drop the entire record
+ if (axis_min_val > filter_max_val || axis_max_val < filter_min_val ||
+ filter_min_val > filter_max_val)
+ return DROP_RECORD_WITH_VAR;
+
+ //condition met and axis pinned, drop the condition
+ if (c->axes_location->has (axis_tag) &&
+ c->axes_location->get (axis_tag).is_point ())
+ return DROP_COND_WITH_VAR;
+
+ if (filter_max_val != axis_max_val || filter_min_val != axis_min_val)
{
// add axisIndex->value into the hashmap so we can check if the record is
// unique with variations
- int16_t min_val = filterRangeMinValue.to_int ();
- int16_t max_val = filterRangeMaxValue.to_int ();
- hb_codepoint_t val = (max_val << 16) + min_val;
+ hb_codepoint_t val = (filter_max_val << 16) + filter_min_val;
condition_map->set (axisIndex, val);
return KEEP_COND_WITH_VAR;
}
- //axis pinned, check if condition is met
- //TODO: add check for axis Ranges
- int v = c->axes_location->get (axis_tag);
-
- //condition not met, drop the entire record
- if (v < filterRangeMinValue.to_int () || v > filterRangeMaxValue.to_int ())
- return DROP_RECORD_WITH_VAR;
-
- //axis pinned and condition met, drop the condition
- return DROP_COND_WITH_VAR;
+ return KEEP_RECORD_WITH_VAR;
}
bool evaluate (const int *coords, unsigned int coord_len) const
@@ -3001,7 +3019,7 @@
{
switch (u.format) {
case 1: return u.format1.keep_with_variations (c, condition_map);
- default:return KEEP_COND_WITH_VAR;
+ default: c->apply = false; return KEEP_COND_WITH_VAR;
}
}
@@ -3046,45 +3064,50 @@
return true;
}
- Cond_with_Var_flag_t keep_with_variations (hb_collect_feature_substitutes_with_var_context_t *c) const
+ void keep_with_variations (hb_collect_feature_substitutes_with_var_context_t *c) const
{
hb_map_t *condition_map = hb_map_create ();
- if (unlikely (!condition_map)) return MEM_ERR_WITH_VAR;
+ if (unlikely (!condition_map)) return;
hb::shared_ptr<hb_map_t> p {condition_map};
hb_set_t *cond_set = hb_set_create ();
- if (unlikely (!cond_set)) return MEM_ERR_WITH_VAR;
+ if (unlikely (!cond_set)) return;
hb::shared_ptr<hb_set_t> s {cond_set};
+ c->apply = true;
+ bool should_keep = false;
unsigned num_kept_cond = 0, cond_idx = 0;
for (const auto& offset : conditions)
{
Cond_with_Var_flag_t ret = (this+offset).keep_with_variations (c, condition_map);
- // one condition is not met, drop the entire record
+ // condition is not met or condition out of range, drop the entire record
if (ret == DROP_RECORD_WITH_VAR)
- return DROP_RECORD_WITH_VAR;
+ return;
- // axis not pinned, keep this condition
if (ret == KEEP_COND_WITH_VAR)
{
+ should_keep = true;
cond_set->add (cond_idx);
num_kept_cond++;
}
+
+ if (ret == KEEP_RECORD_WITH_VAR)
+ should_keep = true;
+
cond_idx++;
}
- // all conditions met
- if (num_kept_cond == 0) return DROP_COND_WITH_VAR;
+ if (!should_keep) return;
//check if condition_set is unique with variations
if (c->conditionset_map->has (p))
//duplicate found, drop the entire record
- return DROP_RECORD_WITH_VAR;
+ return;
c->conditionset_map->set (p, 1);
c->record_cond_idx_map->set (c->cur_record_idx, s);
-
- return KEEP_COND_WITH_VAR;
+ if (should_keep && num_kept_cond == 0)
+ c->universal = true;
}
bool subset (hb_subset_context_t *c,
@@ -3289,12 +3312,11 @@
void collect_feature_substitutes_with_variations (hb_collect_feature_substitutes_with_var_context_t *c,
const void *base) const
{
- // ret == 1, all conditions met
- if ((base+conditions).keep_with_variations (c) == DROP_COND_WITH_VAR &&
- c->apply)
+ (base+conditions).keep_with_variations (c);
+ if (c->apply && !c->variation_applied)
{
(base+substitutions).collect_feature_substitutes_with_variations (c);
- c->apply = false; // set variations only once
+ c->variation_applied = true; // set variations only once
}
}
@@ -3361,7 +3383,12 @@
{
c->cur_record_idx = i;
varRecords[i].collect_feature_substitutes_with_variations (c, this);
+ if (c->universal)
+ break;
}
+ if (c->variation_applied && !c->universal &&
+ !c->record_cond_idx_map->is_empty ())
+ c->insert_catch_all_feature_variation_record = true;
}
FeatureVariations* copy (hb_serialize_context_t *c) const
diff --git a/src/hb-ot-layout-gsubgpos.hh b/src/hb-ot-layout-gsubgpos.hh
index 8e5be92..e10adb7 100644
--- a/src/hb-ot-layout-gsubgpos.hh
+++ b/src/hb-ot-layout-gsubgpos.hh
@@ -143,9 +143,12 @@
return active_glyphs_stack.tail ();
}
- hb_set_t& push_cur_active_glyphs ()
+ hb_set_t* push_cur_active_glyphs ()
{
- return *active_glyphs_stack.push ();
+ hb_set_t *s = active_glyphs_stack.push ();
+ if (unlikely (active_glyphs_stack.in_error ()))
+ return nullptr;
+ return s;
}
bool pop_cur_done_glyphs ()
@@ -427,6 +430,9 @@
MATCH_MAYBE
};
+#ifndef HB_OPTIMIZE_SIZE
+ HB_ALWAYS_INLINE
+#endif
may_match_t may_match (hb_glyph_info_t &info,
hb_codepoint_t glyph_data) const
{
@@ -446,6 +452,9 @@
SKIP_MAYBE
};
+#ifndef HB_OPTIMIZE_SIZE
+ HB_ALWAYS_INLINE
+#endif
may_skip_t may_skip (const hb_ot_apply_context_t *c,
const hb_glyph_info_t &info) const
{
@@ -516,6 +525,9 @@
}
#endif
+#ifndef HB_OPTIMIZE_SIZE
+ HB_ALWAYS_INLINE
+#endif
void reset (unsigned int start_index_,
unsigned int num_items_)
{
@@ -525,6 +537,9 @@
matcher.set_syllable (start_index_ == c->buffer->idx ? c->buffer->cur().syllable () : 0);
}
+#ifndef HB_OPTIMIZE_SIZE
+ HB_ALWAYS_INLINE
+#endif
void reset_fast (unsigned int start_index_,
unsigned int num_items_)
{
@@ -540,6 +555,9 @@
}
matcher_t::may_skip_t
+#ifndef HB_OPTIMIZE_SIZE
+ HB_ALWAYS_INLINE
+#endif
may_skip (const hb_glyph_info_t &info) const
{ return matcher.may_skip (c, info); }
@@ -549,6 +567,9 @@
SKIP
};
+#ifndef HB_OPTIMIZE_SIZE
+ HB_ALWAYS_INLINE
+#endif
match_t match (hb_glyph_info_t &info)
{
matcher_t::may_skip_t skip = matcher.may_skip (c, info);
@@ -567,6 +588,9 @@
return SKIP;
}
+#ifndef HB_OPTIMIZE_SIZE
+ HB_ALWAYS_INLINE
+#endif
bool next (unsigned *unsafe_to = nullptr)
{
assert (num_items > 0);
@@ -600,6 +624,9 @@
*unsafe_to = end;
return false;
}
+#ifndef HB_OPTIMIZE_SIZE
+ HB_ALWAYS_INLINE
+#endif
bool prev (unsigned *unsafe_from = nullptr)
{
assert (num_items > 0);
@@ -703,6 +730,7 @@
hb_font_t *font;
hb_face_t *face;
hb_buffer_t *buffer;
+ hb_sanitize_context_t sanitizer;
recurse_func_t recurse_func = nullptr;
const GDEF &gdef;
const GDEF::accelerator_t &gdef_accel;
@@ -729,9 +757,11 @@
hb_ot_apply_context_t (unsigned int table_index_,
hb_font_t *font_,
- hb_buffer_t *buffer_) :
+ hb_buffer_t *buffer_,
+ hb_blob_t *table_blob_) :
table_index (table_index_),
font (font_), face (font->face), buffer (buffer_),
+ sanitizer (table_blob_),
gdef (
#ifndef HB_NO_OT_LAYOUT
*face->table.GDEF->table
@@ -808,6 +838,9 @@
return true;
}
+#ifndef HB_OPTIMIZE_SIZE
+ HB_ALWAYS_INLINE
+#endif
bool check_glyph_property (const hb_glyph_info_t *info,
unsigned int match_props) const
{
@@ -1213,14 +1246,17 @@
return true;
}
template <typename HBUINT>
-static inline bool match_input (hb_ot_apply_context_t *c,
- unsigned int count, /* Including the first glyph (not matched) */
- const HBUINT input[], /* Array of input values--start with second glyph */
- match_func_t match_func,
- const void *match_data,
- unsigned int *end_position,
- unsigned int match_positions[HB_MAX_CONTEXT_LENGTH],
- unsigned int *p_total_component_count = nullptr)
+#ifndef HB_OPTIMIZE_SIZE
+HB_ALWAYS_INLINE
+#endif
+static bool match_input (hb_ot_apply_context_t *c,
+ unsigned int count, /* Including the first glyph (not matched) */
+ const HBUINT input[], /* Array of input values--start with second glyph */
+ match_func_t match_func,
+ const void *match_data,
+ unsigned int *end_position,
+ unsigned int match_positions[HB_MAX_CONTEXT_LENGTH],
+ unsigned int *p_total_component_count = nullptr)
{
TRACE_APPLY (nullptr);
@@ -1456,12 +1492,15 @@
}
template <typename HBUINT>
-static inline bool match_backtrack (hb_ot_apply_context_t *c,
- unsigned int count,
- const HBUINT backtrack[],
- match_func_t match_func,
- const void *match_data,
- unsigned int *match_start)
+#ifndef HB_OPTIMIZE_SIZE
+HB_ALWAYS_INLINE
+#endif
+static bool match_backtrack (hb_ot_apply_context_t *c,
+ unsigned int count,
+ const HBUINT backtrack[],
+ match_func_t match_func,
+ const void *match_data,
+ unsigned int *match_start)
{
TRACE_APPLY (nullptr);
@@ -1485,13 +1524,16 @@
}
template <typename HBUINT>
-static inline bool match_lookahead (hb_ot_apply_context_t *c,
- unsigned int count,
- const HBUINT lookahead[],
- match_func_t match_func,
- const void *match_data,
- unsigned int start_index,
- unsigned int *end_index)
+#ifndef HB_OPTIMIZE_SIZE
+HB_ALWAYS_INLINE
+#endif
+static bool match_lookahead (hb_ot_apply_context_t *c,
+ unsigned int count,
+ const HBUINT lookahead[],
+ match_func_t match_func,
+ const void *match_data,
+ unsigned int start_index,
+ unsigned int *end_index)
{
TRACE_APPLY (nullptr);
@@ -1615,10 +1657,13 @@
}
covered_seq_indicies.add (seqIndex);
+ hb_set_t *cur_active_glyphs = c->push_cur_active_glyphs ();
+ if (unlikely (!cur_active_glyphs))
+ return;
if (has_pos_glyphs) {
- c->push_cur_active_glyphs () = std::move (pos_glyphs);
+ *cur_active_glyphs = std::move (pos_glyphs);
} else {
- c->push_cur_active_glyphs ().set (*c->glyphs);
+ *cur_active_glyphs = *c->glyphs;
}
unsigned endIndex = inputCount;
@@ -2001,8 +2046,7 @@
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
- return_trace (inputCount.sanitize (c) &&
- lookupCount.sanitize (c) &&
+ return_trace (c->check_struct (this) &&
c->check_range (inputZ.arrayZ,
inputZ.item_size * (inputCount ? inputCount - 1 : 0) +
LookupRecord::static_size * lookupCount));
@@ -2021,6 +2065,7 @@
* design order */
public:
DEFINE_SIZE_ARRAY (4, inputZ);
+ DEFINE_SIZE_MAX (65536 * (Types::HBUINT::static_size + LookupRecord::static_size));
};
template <typename Types>
@@ -2168,8 +2213,9 @@
void closure (hb_closure_context_t *c) const
{
- hb_set_t& cur_active_glyphs = c->push_cur_active_glyphs ();
- get_coverage ().intersect_set (c->previous_parent_active_glyphs (), cur_active_glyphs);
+ hb_set_t* cur_active_glyphs = c->push_cur_active_glyphs ();
+ if (unlikely (!cur_active_glyphs)) return;
+ get_coverage ().intersect_set (c->previous_parent_active_glyphs (), *cur_active_glyphs);
struct ContextClosureLookupContext lookup_context = {
{intersects_glyph, intersected_glyph},
@@ -2338,9 +2384,10 @@
if (!(this+coverage).intersects (c->glyphs))
return;
- hb_set_t& cur_active_glyphs = c->push_cur_active_glyphs ();
+ hb_set_t* cur_active_glyphs = c->push_cur_active_glyphs ();
+ if (unlikely (!cur_active_glyphs)) return;
get_coverage ().intersect_set (c->previous_parent_active_glyphs (),
- cur_active_glyphs);
+ *cur_active_glyphs);
const ClassDef &class_def = this+classDef;
@@ -2583,10 +2630,10 @@
if (!(this+coverageZ[0]).intersects (c->glyphs))
return;
- hb_set_t& cur_active_glyphs = c->push_cur_active_glyphs ();
+ hb_set_t* cur_active_glyphs = c->push_cur_active_glyphs ();
+ if (unlikely (!cur_active_glyphs)) return;
get_coverage ().intersect_set (c->previous_parent_active_glyphs (),
- cur_active_glyphs);
-
+ *cur_active_glyphs);
const LookupRecord *lookupRecord = &StructAfter<LookupRecord> (coverageZ.as_array (glyphCount));
struct ContextClosureLookupContext lookup_context = {
@@ -2687,14 +2734,14 @@
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
- if (!c->check_struct (this)) return_trace (false);
+ if (unlikely (!c->check_struct (this))) return_trace (false);
unsigned int count = glyphCount;
- if (!count) return_trace (false); /* We want to access coverageZ[0] freely. */
- if (!c->check_array (coverageZ.arrayZ, count)) return_trace (false);
+ if (unlikely (!count)) return_trace (false); /* We want to access coverageZ[0] freely. */
+ if (unlikely (!c->check_array (coverageZ.arrayZ, count))) return_trace (false);
for (unsigned int i = 0; i < count; i++)
- if (!coverageZ[i].sanitize (c, this)) return_trace (false);
+ if (unlikely (!coverageZ[i].sanitize (c, this))) return_trace (false);
const LookupRecord *lookupRecord = &StructAfter<LookupRecord> (coverageZ.as_array (glyphCount));
- return_trace (c->check_array (lookupRecord, lookupCount));
+ return_trace (likely (c->check_array (lookupRecord, lookupCount)));
}
protected:
@@ -3014,8 +3061,6 @@
const hb_map_t *lookahead_map = nullptr) const
{
TRACE_SERIALIZE (this);
- auto *out = c->start_embed (this);
- if (unlikely (!out)) return_trace (false);
const hb_map_t *mapping = backtrack_map;
serialize_array (c, backtrack.len, + backtrack.iter ()
@@ -3077,13 +3122,14 @@
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
- if (!backtrack.sanitize (c)) return_trace (false);
+ /* Hyper-optimized sanitized because this is really hot. */
+ if (unlikely (!backtrack.len.sanitize (c))) return_trace (false);
const auto &input = StructAfter<decltype (inputX)> (backtrack);
- if (!input.sanitize (c)) return_trace (false);
+ if (unlikely (!input.lenP1.sanitize (c))) return_trace (false);
const auto &lookahead = StructAfter<decltype (lookaheadX)> (input);
- if (!lookahead.sanitize (c)) return_trace (false);
+ if (unlikely (!lookahead.len.sanitize (c))) return_trace (false);
const auto &lookup = StructAfter<decltype (lookupX)> (lookahead);
- return_trace (lookup.sanitize (c));
+ return_trace (likely (lookup.sanitize (c)));
}
protected:
@@ -3091,7 +3137,7 @@
backtrack; /* Array of backtracking values
* (to be matched before the input
* sequence) */
- HeadlessArrayOf<typename Types::HBUINT>
+ HeadlessArray16Of<typename Types::HBUINT>
inputX; /* Array of input values (start with
* second glyph) */
Array16Of<typename Types::HBUINT>
@@ -3102,6 +3148,7 @@
* design order) */
public:
DEFINE_SIZE_MIN (8);
+ DEFINE_SIZE_MAX (65536 * (3 * Types::HBUINT::static_size + LookupRecord::static_size));
};
template <typename Types>
@@ -3251,9 +3298,10 @@
void closure (hb_closure_context_t *c) const
{
- hb_set_t& cur_active_glyphs = c->push_cur_active_glyphs ();
+ hb_set_t* cur_active_glyphs = c->push_cur_active_glyphs ();
+ if (unlikely (!cur_active_glyphs)) return;
get_coverage ().intersect_set (c->previous_parent_active_glyphs (),
- cur_active_glyphs);
+ *cur_active_glyphs);
struct ChainContextClosureLookupContext lookup_context = {
{intersects_glyph, intersected_glyph},
@@ -3423,10 +3471,10 @@
if (!(this+coverage).intersects (c->glyphs))
return;
- hb_set_t& cur_active_glyphs = c->push_cur_active_glyphs ();
+ hb_set_t* cur_active_glyphs = c->push_cur_active_glyphs ();
+ if (unlikely (!cur_active_glyphs)) return;
get_coverage ().intersect_set (c->previous_parent_active_glyphs (),
- cur_active_glyphs);
-
+ *cur_active_glyphs);
const ClassDef &backtrack_class_def = this+backtrackClassDef;
const ClassDef &input_class_def = this+inputClassDef;
@@ -3727,10 +3775,11 @@
if (!(this+input[0]).intersects (c->glyphs))
return;
- hb_set_t& cur_active_glyphs = c->push_cur_active_glyphs ();
+ hb_set_t* cur_active_glyphs = c->push_cur_active_glyphs ();
+ if (unlikely (!cur_active_glyphs))
+ return;
get_coverage ().intersect_set (c->previous_parent_active_glyphs (),
- cur_active_glyphs);
-
+ *cur_active_glyphs);
const auto &lookahead = StructAfter<decltype (lookaheadX)> (input);
const auto &lookup = StructAfter<decltype (lookupX)> (lookahead);
@@ -3849,8 +3898,6 @@
{
TRACE_SUBSET (this);
- auto *out = c->serializer->start_embed (this);
- if (unlikely (!out)) return_trace (false);
if (unlikely (!c->serializer->embed (this->format))) return_trace (false);
if (!serialize_coverage_offsets (c, backtrack.iter (), this))
@@ -3877,14 +3924,14 @@
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
- if (!backtrack.sanitize (c, this)) return_trace (false);
+ if (unlikely (!backtrack.sanitize (c, this))) return_trace (false);
const auto &input = StructAfter<decltype (inputX)> (backtrack);
- if (!input.sanitize (c, this)) return_trace (false);
- if (!input.len) return_trace (false); /* To be consistent with Context. */
+ if (unlikely (!input.sanitize (c, this))) return_trace (false);
+ if (unlikely (!input.len)) return_trace (false); /* To be consistent with Context. */
const auto &lookahead = StructAfter<decltype (lookaheadX)> (input);
- if (!lookahead.sanitize (c, this)) return_trace (false);
+ if (unlikely (!lookahead.sanitize (c, this))) return_trace (false);
const auto &lookup = StructAfter<decltype (lookupX)> (lookahead);
- return_trace (lookup.sanitize (c));
+ return_trace (likely (lookup.sanitize (c)));
}
protected:
@@ -3974,7 +4021,7 @@
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (this);
- if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
+ if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
out->format = format;
out->extensionLookupType = extensionLookupType;
@@ -4503,7 +4550,10 @@
{
accelerator_t (hb_face_t *face)
{
- this->table = hb_sanitize_context_t ().reference_table<T> (face);
+ hb_sanitize_context_t sc;
+ sc.lazy_some_gpos = true;
+ this->table = sc.reference_table<T> (face);
+
if (unlikely (this->table->is_blocklisted (this->table.get_blob (), face)))
{
hb_blob_destroy (this->table.get_blob ());
@@ -4528,6 +4578,8 @@
this->table.destroy ();
}
+ hb_blob_t *get_blob () const { return table.get_blob (); }
+
hb_ot_layout_lookup_accelerator_t *get_accel (unsigned lookup_index) const
{
if (unlikely (lookup_index >= lookup_count)) return nullptr;
diff --git a/src/hb-ot-layout.cc b/src/hb-ot-layout.cc
index c66ee8c..020b8a6 100644
--- a/src/hb-ot-layout.cc
+++ b/src/hb-ot-layout.cc
@@ -1316,8 +1316,7 @@
hb_set_t feature_indexes;
hb_ot_layout_collect_features (face, table_tag, scripts, languages, features, &feature_indexes);
- for (hb_codepoint_t feature_index = HB_SET_VALUE_INVALID;
- hb_set_next (&feature_indexes, &feature_index);)
+ for (auto feature_index : feature_indexes)
g.get_feature (feature_index).add_lookup_indexes_to (lookup_indexes);
g.feature_variation_collect_lookups (&feature_indexes, nullptr, lookup_indexes);
@@ -1570,7 +1569,7 @@
glyphs_length = glyphs->get_population ();
if (lookups)
{
- for (hb_codepoint_t lookup_index = HB_SET_VALUE_INVALID; hb_set_next (lookups, &lookup_index);)
+ for (auto lookup_index : *lookups)
gsub.get_lookup (lookup_index).closure (&c, lookup_index);
}
else
@@ -1953,7 +1952,7 @@
{
const unsigned int table_index = proxy.table_index;
unsigned int i = 0;
- OT::hb_ot_apply_context_t c (table_index, font, buffer);
+ OT::hb_ot_apply_context_t c (table_index, font, buffer, proxy.accel.get_blob ());
c.set_recurse_func (Proxy::Lookup::template dispatch_recurse_func<OT::hb_ot_apply_context_t>);
for (unsigned int stage_index = 0; stage_index < stages[table_index].length; stage_index++)
@@ -2011,20 +2010,20 @@
{
GSUBProxy proxy (font->face);
if (buffer->messaging () &&
- !buffer->message (font, "start table GSUB")) return;
+ !buffer->message (font, "start table GSUB script tag '%c%c%c%c'", HB_UNTAG (chosen_script[0]))) return;
apply (proxy, plan, font, buffer);
if (buffer->messaging ())
- (void) buffer->message (font, "end table GSUB");
+ (void) buffer->message (font, "end table GSUB script tag '%c%c%c%c'", HB_UNTAG (chosen_script[0]));
}
void hb_ot_map_t::position (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const
{
GPOSProxy proxy (font->face);
if (buffer->messaging () &&
- !buffer->message (font, "start table GPOS")) return;
+ !buffer->message (font, "start table GPOS script tag '%c%c%c%c'", HB_UNTAG (chosen_script[1]))) return;
apply (proxy, plan, font, buffer);
if (buffer->messaging ())
- (void) buffer->message (font, "end table GPOS");
+ (void) buffer->message (font, "end table GPOS script tag '%c%c%c%c'", HB_UNTAG (chosen_script[1]));
}
void
@@ -2036,6 +2035,112 @@
}
#ifndef HB_NO_BASE
+
+static void
+choose_base_tags (hb_script_t script,
+ hb_language_t language,
+ hb_tag_t *script_tag,
+ hb_tag_t *language_tag)
+{
+ hb_tag_t script_tags[HB_OT_MAX_TAGS_PER_SCRIPT];
+ unsigned script_count = ARRAY_LENGTH (script_tags);
+
+ hb_tag_t language_tags[HB_OT_MAX_TAGS_PER_LANGUAGE];
+ unsigned language_count = ARRAY_LENGTH (language_tags);
+
+ hb_ot_tags_from_script_and_language (script, language,
+ &script_count, script_tags,
+ &language_count, language_tags);
+
+ *script_tag = script_count ? script_tags[script_count - 1] : HB_OT_TAG_DEFAULT_SCRIPT;
+ *language_tag = language_count ? language_tags[language_count - 1] : HB_OT_TAG_DEFAULT_LANGUAGE;
+}
+
+/**
+ * hb_ot_layout_get_font_extents:
+ * @font: a font
+ * @direction: text direction.
+ * @script_tag: script tag.
+ * @language_tag: language tag.
+ * @extents: (out) (nullable): font extents if found.
+ *
+ * Fetches script/language-specific font extents. These values are
+ * looked up in the `BASE` table's `MinMax` records.
+ *
+ * If no such extents are found, the default extents for the font are
+ * fetched. As such, the return value of this function can for the
+ * most part be ignored. Note that the per-script/language extents
+ * do not have a line-gap value, and the line-gap is set to zero in
+ * that case.
+ *
+ * Return value: `true` if found script/language-specific font extents.
+ *
+ * Since: 8.0.0
+ **/
+hb_bool_t
+hb_ot_layout_get_font_extents (hb_font_t *font,
+ hb_direction_t direction,
+ hb_tag_t script_tag,
+ hb_tag_t language_tag,
+ hb_font_extents_t *extents)
+{
+ hb_position_t min, max;
+ if (font->face->table.BASE->get_min_max (font, direction, script_tag, language_tag, HB_TAG_NONE,
+ &min, &max))
+ {
+ if (extents)
+ {
+ extents->ascender = max;
+ extents->descender = min;
+ extents->line_gap = 0;
+ }
+ return true;
+ }
+
+ hb_font_get_extents_for_direction (font, direction, extents);
+ return false;
+}
+
+/**
+ * hb_ot_layout_get_font_extents2:
+ * @font: a font
+ * @direction: text direction.
+ * @script: script.
+ * @language: (nullable): language.
+ * @extents: (out) (nullable): font extents if found.
+ *
+ * Fetches script/language-specific font extents. These values are
+ * looked up in the `BASE` table's `MinMax` records.
+ *
+ * If no such extents are found, the default extents for the font are
+ * fetched. As such, the return value of this function can for the
+ * most part be ignored. Note that the per-script/language extents
+ * do not have a line-gap value, and the line-gap is set to zero in
+ * that case.
+ *
+ * This function is like hb_ot_layout_get_font_extents() but takes
+ * #hb_script_t and #hb_language_t instead of OpenType #hb_tag_t.
+ *
+ * Return value: `true` if found script/language-specific font extents.
+ *
+ * Since: 8.0.0
+ **/
+hb_bool_t
+hb_ot_layout_get_font_extents2 (hb_font_t *font,
+ hb_direction_t direction,
+ hb_script_t script,
+ hb_language_t language,
+ hb_font_extents_t *extents)
+{
+ hb_tag_t script_tag, language_tag;
+ choose_base_tags (script, language, &script_tag, &language_tag);
+ return hb_ot_layout_get_font_extents (font,
+ direction,
+ script_tag,
+ language_tag,
+ extents);
+}
+
/**
* hb_ot_layout_get_horizontal_baseline_tag_for_script:
* @script: a script tag.
@@ -2134,6 +2239,42 @@
}
/**
+ * hb_ot_layout_get_baseline2:
+ * @font: a font
+ * @baseline_tag: a baseline tag
+ * @direction: text direction.
+ * @script: script.
+ * @language: (nullable): language, currently unused.
+ * @coord: (out) (nullable): baseline value if found.
+ *
+ * Fetches a baseline value from the face.
+ *
+ * This function is like hb_ot_layout_get_baseline() but takes
+ * #hb_script_t and #hb_language_t instead of OpenType #hb_tag_t.
+ *
+ * Return value: `true` if found baseline value in the font.
+ *
+ * Since: 8.0.0
+ **/
+hb_bool_t
+hb_ot_layout_get_baseline2 (hb_font_t *font,
+ hb_ot_layout_baseline_tag_t baseline_tag,
+ hb_direction_t direction,
+ hb_script_t script,
+ hb_language_t language,
+ hb_position_t *coord /* OUT. May be NULL. */)
+{
+ hb_tag_t script_tag, language_tag;
+ choose_base_tags (script, language, &script_tag, &language_tag);
+ return hb_ot_layout_get_baseline (font,
+ baseline_tag,
+ direction,
+ script_tag,
+ language_tag,
+ coord);
+}
+
+/**
* hb_ot_layout_get_baseline_with_fallback:
* @font: a font
* @baseline_tag: a baseline tag
@@ -2355,6 +2496,41 @@
}
}
+/**
+ * hb_ot_layout_get_baseline_with_fallback2:
+ * @font: a font
+ * @baseline_tag: a baseline tag
+ * @direction: text direction.
+ * @script: script.
+ * @language: (nullable): language, currently unused.
+ * @coord: (out): baseline value if found.
+ *
+ * Fetches a baseline value from the face, and synthesizes
+ * it if the font does not have it.
+ *
+ * This function is like hb_ot_layout_get_baseline_with_fallback() but takes
+ * #hb_script_t and #hb_language_t instead of OpenType #hb_tag_t.
+ *
+ * Since: 8.0.0
+ **/
+void
+hb_ot_layout_get_baseline_with_fallback2 (hb_font_t *font,
+ hb_ot_layout_baseline_tag_t baseline_tag,
+ hb_direction_t direction,
+ hb_script_t script,
+ hb_language_t language,
+ hb_position_t *coord /* OUT */)
+{
+ hb_tag_t script_tag, language_tag;
+ choose_base_tags (script, language, &script_tag, &language_tag);
+ hb_ot_layout_get_baseline_with_fallback (font,
+ baseline_tag,
+ direction,
+ script_tag,
+ language_tag,
+ coord);
+}
+
#endif
@@ -2451,9 +2627,10 @@
hb_codepoint_t glyph)
{
const OT::PosLookup &lookup = font->face->table.GPOS->table->get_lookup (lookup_index);
+ hb_blob_t *blob = font->face->table.GPOS->get_blob ();
hb_glyph_position_t pos = {0};
hb_position_single_dispatch_t c;
- lookup.dispatch (&c, font, direction, glyph, pos);
+ lookup.dispatch (&c, font, blob, direction, glyph, pos);
hb_position_t ret = 0;
switch (direction)
{
diff --git a/src/hb-ot-layout.h b/src/hb-ot-layout.h
index 10dcc65..b0fae37 100644
--- a/src/hb-ot-layout.h
+++ b/src/hb-ot-layout.h
@@ -447,6 +447,20 @@
* BASE
*/
+HB_EXTERN hb_bool_t
+hb_ot_layout_get_font_extents (hb_font_t *font,
+ hb_direction_t direction,
+ hb_tag_t script_tag,
+ hb_tag_t language_tag,
+ hb_font_extents_t *extents);
+
+HB_EXTERN hb_bool_t
+hb_ot_layout_get_font_extents2 (hb_font_t *font,
+ hb_direction_t direction,
+ hb_script_t script,
+ hb_language_t language,
+ hb_font_extents_t *extents);
+
/**
* hb_ot_layout_baseline_tag_t:
* @HB_OT_LAYOUT_BASELINE_TAG_ROMAN: The baseline used by alphabetic scripts such as Latin, Cyrillic and Greek.
@@ -499,6 +513,14 @@
hb_tag_t language_tag,
hb_position_t *coord /* OUT. May be NULL. */);
+HB_EXTERN hb_bool_t
+hb_ot_layout_get_baseline2 (hb_font_t *font,
+ hb_ot_layout_baseline_tag_t baseline_tag,
+ hb_direction_t direction,
+ hb_script_t script,
+ hb_language_t language,
+ hb_position_t *coord /* OUT. May be NULL. */);
+
HB_EXTERN void
hb_ot_layout_get_baseline_with_fallback (hb_font_t *font,
hb_ot_layout_baseline_tag_t baseline_tag,
@@ -507,6 +529,14 @@
hb_tag_t language_tag,
hb_position_t *coord /* OUT */);
+HB_EXTERN void
+hb_ot_layout_get_baseline_with_fallback2 (hb_font_t *font,
+ hb_ot_layout_baseline_tag_t baseline_tag,
+ hb_direction_t direction,
+ hb_script_t script,
+ hb_language_t language,
+ hb_position_t *coord /* OUT */);
+
HB_END_DECLS
#endif /* HB_OT_LAYOUT_H */
diff --git a/src/hb-ot-layout.hh b/src/hb-ot-layout.hh
index 9505d5f..d718893 100644
--- a/src/hb-ot-layout.hh
+++ b/src/hb-ot-layout.hh
@@ -448,7 +448,7 @@
static inline bool
_hb_glyph_info_ligated_internal (const hb_glyph_info_t *info)
{
- return !!(info->lig_props() & IS_LIG_BASE);
+ return info->lig_props() & IS_LIG_BASE;
}
static inline unsigned int
@@ -496,37 +496,37 @@
static inline bool
_hb_glyph_info_is_base_glyph (const hb_glyph_info_t *info)
{
- return !!(info->glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH);
+ return info->glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH;
}
static inline bool
_hb_glyph_info_is_ligature (const hb_glyph_info_t *info)
{
- return !!(info->glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_LIGATURE);
+ return info->glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_LIGATURE;
}
static inline bool
_hb_glyph_info_is_mark (const hb_glyph_info_t *info)
{
- return !!(info->glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_MARK);
+ return info->glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_MARK;
}
static inline bool
_hb_glyph_info_substituted (const hb_glyph_info_t *info)
{
- return !!(info->glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_SUBSTITUTED);
+ return info->glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_SUBSTITUTED;
}
static inline bool
_hb_glyph_info_ligated (const hb_glyph_info_t *info)
{
- return !!(info->glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_LIGATED);
+ return info->glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_LIGATED;
}
static inline bool
_hb_glyph_info_multiplied (const hb_glyph_info_t *info)
{
- return !!(info->glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_MULTIPLIED);
+ return info->glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_MULTIPLIED;
}
static inline bool
diff --git a/src/hb-ot-map.cc b/src/hb-ot-map.cc
index 8882dba..bacd56e 100644
--- a/src/hb-ot-map.cc
+++ b/src/hb-ot-map.cc
@@ -213,7 +213,8 @@
/* Sort features and merge duplicates */
if (feature_infos.length)
{
- feature_infos.qsort ();
+ if (!is_simple)
+ feature_infos.qsort ();
auto *f = feature_infos.arrayZ;
unsigned int j = 0;
unsigned count = feature_infos.length;
@@ -314,7 +315,8 @@
map->needs_fallback = !found;
}
//feature_infos.shrink (0); /* Done with these */
-
+ if (is_simple)
+ m.features.qsort ();
add_gsub_pause (nullptr);
add_gpos_pause (nullptr);
@@ -350,7 +352,7 @@
}
/* Sort lookups and merge duplicates */
- if (last_num_lookups < lookups.length)
+ if (last_num_lookups + 1 < lookups.length)
{
lookups.as_array ().sub_array (last_num_lookups, lookups.length - last_num_lookups).qsort ();
diff --git a/src/hb-ot-map.hh b/src/hb-ot-map.hh
index efc8cae..8af8129 100644
--- a/src/hb-ot-map.hh
+++ b/src/hb-ot-map.hh
@@ -60,6 +60,13 @@
int cmp (const hb_tag_t tag_) const
{ return tag_ < tag ? -1 : tag_ > tag ? 1 : 0; }
+
+ HB_INTERNAL static int cmp (const void *pa, const void *pb)
+ {
+ const feature_map_t *a = (const feature_map_t *) pa;
+ const feature_map_t *b = (const feature_map_t *) pb;
+ return a->tag < b->tag ? -1 : a->tag > b->tag ? 1 : 0;
+ }
};
struct lookup_map_t {
@@ -273,6 +280,7 @@
hb_face_t *face;
hb_segment_properties_t props;
+ bool is_simple;
hb_tag_t chosen_script[2];
bool found_script[2];
diff --git a/src/hb-ot-math-table.hh b/src/hb-ot-math-table.hh
index dccf720..b11da46 100644
--- a/src/hb-ot-math-table.hh
+++ b/src/hb-ot-math-table.hh
@@ -73,7 +73,6 @@
{
TRACE_SERIALIZE (this);
auto *out = c->start_embed (this);
- if (unlikely (!out)) return_trace (nullptr);
HBINT16 *p = c->allocate_size<HBINT16> (HBINT16::static_size * 2);
if (unlikely (!p)) return_trace (nullptr);
@@ -310,7 +309,6 @@
{
TRACE_SERIALIZE (this);
auto *out = c->start_embed (this);
- if (unlikely (!out)) return_trace (nullptr);
if (unlikely (!c->embed (heightCount))) return_trace (nullptr);
@@ -572,6 +570,7 @@
auto it =
+ hb_iter (this+extendedShapeCoverage)
+ | hb_take (c->plan->source->get_num_glyphs ())
| hb_filter (glyphset)
| hb_map_retains_sorting (glyph_map)
;
@@ -757,8 +756,6 @@
bool subset (hb_subset_context_t *c) const
{
TRACE_SUBSET (this);
- auto *out = c->serializer->start_embed (*this);
- if (unlikely (!out)) return_trace (false);
if (!c->serializer->copy (italicsCorrection, this)) return_trace (false);
if (!c->serializer->copy<HBUINT16> (partRecords.len)) return_trace (false);
@@ -945,13 +942,13 @@
if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
if (!c->serializer->check_assign (out->minConnectorOverlap, minConnectorOverlap, HB_SERIALIZE_ERROR_INT_OVERFLOW))
return_trace (false);
-
+
hb_sorted_vector_t<hb_codepoint_t> new_vert_coverage;
hb_sorted_vector_t<hb_codepoint_t> new_hori_coverage;
hb_set_t indices;
collect_coverage_and_indices (new_vert_coverage, vertGlyphCoverage, 0, vertGlyphCount, indices, glyphset, glyph_map);
collect_coverage_and_indices (new_hori_coverage, horizGlyphCoverage, vertGlyphCount, vertGlyphCount + horizGlyphCount, indices, glyphset, glyph_map);
-
+
if (!c->serializer->check_assign (out->vertGlyphCount, new_vert_coverage.length, HB_SERIALIZE_ERROR_INT_OVERFLOW))
return_trace (false);
if (!c->serializer->check_assign (out->horizGlyphCount, new_hori_coverage.length, HB_SERIALIZE_ERROR_INT_OVERFLOW))
@@ -963,10 +960,10 @@
if (!o) return_trace (false);
o->serialize_subset (c, glyphConstruction[i], this);
}
-
+
if (new_vert_coverage)
out->vertGlyphCoverage.serialize_serialize (c->serializer, new_vert_coverage.iter ());
-
+
if (new_hori_coverage)
out->horizGlyphCoverage.serialize_serialize (c->serializer, new_hori_coverage.iter ());
return_trace (true);
diff --git a/src/hb-ot-os2-table.hh b/src/hb-ot-os2-table.hh
index 97d18b9..72cb662 100644
--- a/src/hb-ot-os2-table.hh
+++ b/src/hb-ot-os2-table.hh
@@ -249,7 +249,7 @@
if (c->plan->user_axes_location.has (HB_TAG ('w','g','h','t')) &&
!c->plan->pinned_at_default)
{
- float weight_class = c->plan->user_axes_location.get (HB_TAG ('w','g','h','t'));
+ float weight_class = c->plan->user_axes_location.get (HB_TAG ('w','g','h','t')).middle;
if (!c->serializer->check_assign (os2_prime->usWeightClass,
roundf (hb_clamp (weight_class, 1.0f, 1000.0f)),
HB_SERIALIZE_ERROR_INT_OVERFLOW))
@@ -259,7 +259,7 @@
if (c->plan->user_axes_location.has (HB_TAG ('w','d','t','h')) &&
!c->plan->pinned_at_default)
{
- float width = c->plan->user_axes_location.get (HB_TAG ('w','d','t','h'));
+ float width = c->plan->user_axes_location.get (HB_TAG ('w','d','t','h')).middle;
if (!c->serializer->check_assign (os2_prime->usWidthClass,
roundf (map_wdth_to_widthclass (width)),
HB_SERIALIZE_ERROR_INT_OVERFLOW))
@@ -287,8 +287,7 @@
/* This block doesn't show up in profiles. If it ever did,
* we can rewrite it to iterate over OS/2 ranges and use
* set iteration to check if the range matches. */
- for (hb_codepoint_t cp = HB_SET_VALUE_INVALID;
- codepoints->next (&cp);)
+ for (auto cp : *codepoints)
{
unsigned int bit = _hb_ot_os2_get_unicode_range_bit (cp);
if (bit < 128)
diff --git a/src/hb-ot-post-table-v2subset.hh b/src/hb-ot-post-table-v2subset.hh
index 951e639..d442336 100644
--- a/src/hb-ot-post-table-v2subset.hh
+++ b/src/hb-ot-post-table-v2subset.hh
@@ -79,6 +79,11 @@
post::accelerator_t _post (c->plan->source);
hb_hashmap_t<hb_bytes_t, uint32_t, true> glyph_name_to_new_index;
+
+ old_new_index_map.alloc (num_glyphs);
+ old_gid_new_index_map.alloc (num_glyphs);
+ glyph_name_to_new_index.alloc (num_glyphs);
+
for (hb_codepoint_t new_gid = 0; new_gid < num_glyphs; new_gid++)
{
hb_codepoint_t old_gid = reverse_glyph_map.get (new_gid);
@@ -86,11 +91,12 @@
unsigned new_index;
const uint32_t *new_index2;
- if (old_index <= 257) new_index = old_index;
+ if (old_index <= 257)
+ new_index = old_index;
else if (old_new_index_map.has (old_index, &new_index2))
- {
new_index = *new_index2;
- } else {
+ else
+ {
hb_bytes_t s = _post.find_glyph_name (old_gid);
new_index = glyph_name_to_new_index.get (s);
if (new_index == (unsigned)-1)
diff --git a/src/hb-ot-post-table.hh b/src/hb-ot-post-table.hh
index 042fa61..761e49d 100644
--- a/src/hb-ot-post-table.hh
+++ b/src/hb-ot-post-table.hh
@@ -96,8 +96,7 @@
bool subset (hb_subset_context_t *c) const
{
TRACE_SUBSET (this);
- post *post_prime = c->serializer->start_embed<post> ();
- if (unlikely (!post_prime)) return_trace (false);
+ auto *post_prime = c->serializer->start_embed<post> ();
bool glyph_names = c->plan->flags & HB_SUBSET_FLAGS_GLYPH_NAMES;
if (!serialize (c->serializer, glyph_names))
@@ -117,7 +116,7 @@
if (c->plan->user_axes_location.has (HB_TAG ('s','l','n','t')) &&
!c->plan->pinned_at_default)
{
- float italic_angle = c->plan->user_axes_location.get (HB_TAG ('s','l','n','t'));
+ float italic_angle = c->plan->user_axes_location.get (HB_TAG ('s','l','n','t')).middle;
italic_angle = hb_max (-90.f, hb_min (italic_angle, 90.f));
post_prime->italicAngle.set_float (italic_angle);
}
diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index 3d207e0..d84313f 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -313,6 +313,8 @@
{
hb_ot_map_builder_t *map = &planner->map;
+ map->is_simple = true;
+
map->enable_feature (HB_TAG('r','v','r','n'));
map->add_gsub_pause (nullptr);
@@ -354,7 +356,10 @@
map->enable_feature (HB_TAG ('H','A','R','F')); /* Considered discretionary. */
if (planner->shaper->collect_features)
+ {
+ map->is_simple = false;
planner->shaper->collect_features (planner);
+ }
map->enable_feature (HB_TAG ('B','u','z','z')); /* Considered required. */
map->enable_feature (HB_TAG ('B','U','Z','Z')); /* Considered discretionary. */
@@ -378,6 +383,8 @@
map->enable_feature (HB_TAG ('v','e','r','t'), F_GLOBAL_SEARCH);
}
+ if (num_user_features)
+ map->is_simple = false;
for (unsigned int i = 0; i < num_user_features; i++)
{
const hb_feature_t *feature = &user_features[i];
diff --git a/src/hb-ot-shaper-arabic-fallback.hh b/src/hb-ot-shaper-arabic-fallback.hh
index e7a6900..66a8bfb 100644
--- a/src/hb-ot-shaper-arabic-fallback.hh
+++ b/src/hb-ot-shaper-arabic-fallback.hh
@@ -368,7 +368,7 @@
hb_font_t *font,
hb_buffer_t *buffer)
{
- OT::hb_ot_apply_context_t c (0, font, buffer);
+ OT::hb_ot_apply_context_t c (0, font, buffer, hb_blob_get_empty ());
for (unsigned int i = 0; i < fallback_plan->num_lookups; i++)
if (fallback_plan->lookup_array[i]) {
c.set_lookup_mask (fallback_plan->mask_array[i]);
diff --git a/src/hb-ot-shaper-syllabic.cc b/src/hb-ot-shaper-syllabic.cc
index 89226ae..97f6203 100644
--- a/src/hb-ot-shaper-syllabic.cc
+++ b/src/hb-ot-shaper-syllabic.cc
@@ -40,6 +40,14 @@
if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE))
return false;
if (likely (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE)))
+ {
+ if (buffer->messaging ())
+ (void) buffer->message (font, "skipped inserting dotted-circles because there is no broken syllables");
+ return false;
+ }
+
+ if (buffer->messaging () &&
+ !buffer->message (font, "start inserting dotted-circles"))
return false;
hb_codepoint_t dottedcircle_glyph;
@@ -84,6 +92,10 @@
(void) buffer->next_glyph ();
}
buffer->sync ();
+
+ if (buffer->messaging ())
+ (void) buffer->message (font, "end inserting dotted-circles");
+
return true;
}
diff --git a/src/hb-ot-shaper-use-machine.rl b/src/hb-ot-shaper-use-machine.rl
index 256e46d..bd55380 100644
--- a/src/hb-ot-shaper-use-machine.rl
+++ b/src/hb-ot-shaper-use-machine.rl
@@ -110,7 +110,7 @@
h = H | HVM | IS | Sk;
-consonant_modifiers = CMAbv* CMBlw* ((h B | SUB) CMAbv? CMBlw*)*;
+consonant_modifiers = CMAbv* CMBlw* ((h B | SUB) CMAbv* CMBlw*)*;
medial_consonants = MPre? MAbv? MBlw? MPst?;
dependent_vowels = VPre* VAbv* VBlw* VPst* | H;
vowel_modifiers = HVM? VMPre* VMAbv* VMBlw* VMPst*;
diff --git a/src/hb-ot-shaper-use.cc b/src/hb-ot-shaper-use.cc
index 342aba1..c35765a 100644
--- a/src/hb-ot-shaper-use.cc
+++ b/src/hb-ot-shaper-use.cc
@@ -377,6 +377,9 @@
#define POST_BASE_FLAGS64 (FLAG64 (USE(FAbv)) | \
FLAG64 (USE(FBlw)) | \
FLAG64 (USE(FPst)) | \
+ FLAG64 (USE(FMAbv)) | \
+ FLAG64 (USE(FMBlw)) | \
+ FLAG64 (USE(FMPst)) | \
FLAG64 (USE(MAbv)) | \
FLAG64 (USE(MBlw)) | \
FLAG64 (USE(MPst)) | \
diff --git a/src/hb-ot-stat-table.hh b/src/hb-ot-stat-table.hh
index de553dd..f7bb379 100644
--- a/src/hb-ot-stat-table.hh
+++ b/src/hb-ot-stat-table.hh
@@ -57,6 +57,16 @@
// Reserved = 0xFFFC /* Reserved for future use — set to zero. */
};
+static bool axis_value_is_outside_axis_range (hb_tag_t axis_tag, float axis_value,
+ const hb_hashmap_t<hb_tag_t, Triple> *user_axes_location)
+{
+ if (!user_axes_location->has (axis_tag))
+ return false;
+
+ Triple axis_range = user_axes_location->get (axis_tag);
+ return (axis_value < axis_range.minimum || axis_value > axis_range.maximum);
+}
+
struct StatAxisRecord
{
int cmp (hb_tag_t key) const { return tag.cmp (key); }
@@ -96,23 +106,19 @@
}
bool keep_axis_value (const hb_array_t<const StatAxisRecord> axis_records,
- const hb_hashmap_t<hb_tag_t, float> *user_axes_location) const
+ const hb_hashmap_t<hb_tag_t, Triple> *user_axes_location) const
{
hb_tag_t axis_tag = get_axis_tag (axis_records);
float axis_value = get_value ();
- if (!user_axes_location->has (axis_tag) ||
- fabsf(axis_value - user_axes_location->get (axis_tag)) < 0.001f)
- return true;
-
- return false;
+ return !axis_value_is_outside_axis_range (axis_tag, axis_value, user_axes_location);
}
bool subset (hb_subset_context_t *c,
const hb_array_t<const StatAxisRecord> axis_records) const
{
TRACE_SUBSET (this);
- const hb_hashmap_t<hb_tag_t, float>* user_axes_location = &c->plan->user_axes_location;
+ const hb_hashmap_t<hb_tag_t, Triple>* user_axes_location = &c->plan->user_axes_location;
if (keep_axis_value (axis_records, user_axes_location))
return_trace (c->serializer->embed (this));
@@ -155,23 +161,19 @@
}
bool keep_axis_value (const hb_array_t<const StatAxisRecord> axis_records,
- const hb_hashmap_t<hb_tag_t, float> *user_axes_location) const
+ const hb_hashmap_t<hb_tag_t, Triple> *user_axes_location) const
{
hb_tag_t axis_tag = get_axis_tag (axis_records);
float axis_value = get_value ();
- if (!user_axes_location->has (axis_tag) ||
- fabsf(axis_value - user_axes_location->get (axis_tag)) < 0.001f)
- return true;
-
- return false;
+ return !axis_value_is_outside_axis_range (axis_tag, axis_value, user_axes_location);
}
bool subset (hb_subset_context_t *c,
const hb_array_t<const StatAxisRecord> axis_records) const
{
TRACE_SUBSET (this);
- const hb_hashmap_t<hb_tag_t, float>* user_axes_location = &c->plan->user_axes_location;
+ const hb_hashmap_t<hb_tag_t, Triple>* user_axes_location = &c->plan->user_axes_location;
if (keep_axis_value (axis_records, user_axes_location))
return_trace (c->serializer->embed (this));
@@ -218,23 +220,19 @@
}
bool keep_axis_value (const hb_array_t<const StatAxisRecord> axis_records,
- const hb_hashmap_t<hb_tag_t, float> *user_axes_location) const
+ const hb_hashmap_t<hb_tag_t, Triple> *user_axes_location) const
{
hb_tag_t axis_tag = get_axis_tag (axis_records);
float axis_value = get_value ();
- if (!user_axes_location->has (axis_tag) ||
- fabsf(axis_value - user_axes_location->get (axis_tag)) < 0.001f)
- return true;
-
- return false;
+ return !axis_value_is_outside_axis_range (axis_tag, axis_value, user_axes_location);
}
bool subset (hb_subset_context_t *c,
const hb_array_t<const StatAxisRecord> axis_records) const
{
TRACE_SUBSET (this);
- const hb_hashmap_t<hb_tag_t, float>* user_axes_location = &c->plan->user_axes_location;
+ const hb_hashmap_t<hb_tag_t, Triple>* user_axes_location = &c->plan->user_axes_location;
if (keep_axis_value (axis_records, user_axes_location))
return_trace (c->serializer->embed (this));
@@ -291,7 +289,7 @@
{ return axisValues.as_array (axisCount)[axis_index]; }
bool keep_axis_value (const hb_array_t<const StatAxisRecord> axis_records,
- const hb_hashmap_t<hb_tag_t, float> *user_axes_location) const
+ const hb_hashmap_t<hb_tag_t, Triple> *user_axes_location) const
{
hb_array_t<const AxisValueRecord> axis_value_records = axisValues.as_array (axisCount);
@@ -301,8 +299,7 @@
float axis_value = rec.get_value ();
hb_tag_t axis_tag = axis_records[axis_idx].get_axis_tag ();
- if (user_axes_location->has (axis_tag) &&
- fabsf(axis_value - user_axes_location->get (axis_tag)) > 0.001f)
+ if (axis_value_is_outside_axis_range (axis_tag, axis_value, user_axes_location))
return false;
}
@@ -313,7 +310,7 @@
const hb_array_t<const StatAxisRecord> axis_records) const
{
TRACE_SUBSET (this);
- const hb_hashmap_t<hb_tag_t, float> *user_axes_location = &c->plan->user_axes_location;
+ const hb_hashmap_t<hb_tag_t, Triple> *user_axes_location = &c->plan->user_axes_location;
if (!keep_axis_value (axis_records, user_axes_location))
return_trace (false);
@@ -402,7 +399,7 @@
}
bool keep_axis_value (const hb_array_t<const StatAxisRecord> axis_records,
- hb_hashmap_t<hb_tag_t, float> *user_axes_location) const
+ hb_hashmap_t<hb_tag_t, Triple> *user_axes_location) const
{
switch (u.format)
{
@@ -451,8 +448,6 @@
const hb_array_t<const StatAxisRecord> axis_records) const
{
TRACE_SUBSET (this);
- auto *out = c->serializer->start_embed (this);
- if (unlikely (!out)) return_trace (false);
auto axisValueOffsets = as_array (axisValueCount);
count = 0;
@@ -517,7 +512,7 @@
return axis_value.get_value_name_id ();
}
- void collect_name_ids (hb_hashmap_t<hb_tag_t, float> *user_axes_location,
+ void collect_name_ids (hb_hashmap_t<hb_tag_t, Triple> *user_axes_location,
hb_set_t *nameids_to_retain /* OUT */) const
{
if (!has_data ()) return;
diff --git a/src/hb-ot-tag.cc b/src/hb-ot-tag.cc
index 547f957..53b6b38 100644
--- a/src/hb-ot-tag.cc
+++ b/src/hb-ot-tag.cc
@@ -412,7 +412,7 @@
/**
* hb_ot_tags_from_script_and_language:
* @script: an #hb_script_t to convert.
- * @language: an #hb_language_t to convert.
+ * @language: (nullable): an #hb_language_t to convert.
* @script_count: (inout) (optional): maximum number of script tags to retrieve (IN)
* and actual number of script tags retrieved (OUT)
* @script_tags: (out) (optional): array of size at least @script_count to store the
diff --git a/src/hb-ot-var-common.hh b/src/hb-ot-var-common.hh
index 7d4bf22..6bbb4d6 100644
--- a/src/hb-ot-var-common.hh
+++ b/src/hb-ot-var-common.hh
@@ -36,19 +36,14 @@
{
friend struct DeltaSetIndexMap;
+ unsigned get_size () const
+ { return min_size + mapCount * get_width (); }
+
private:
DeltaSetIndexMapFormat01* copy (hb_serialize_context_t *c) const
{
TRACE_SERIALIZE (this);
- auto *out = c->start_embed (this);
- if (unlikely (!out)) return_trace (nullptr);
-
- unsigned total_size = min_size + mapCount * get_width ();
- HBUINT8 *p = c->allocate_size<HBUINT8> (total_size);
- if (unlikely (!p)) return_trace (nullptr);
-
- hb_memcpy (p, this, HBUINT8::static_size * total_size);
- return_trace (out);
+ return_trace (c->embed (this));
}
template <typename T>
@@ -69,14 +64,17 @@
if (unlikely (!p)) return_trace (false);
for (unsigned int i = 0; i < output_map.length; i++)
{
- unsigned int v = output_map[i];
- unsigned int outer = v >> 16;
- unsigned int inner = v & 0xFFFF;
- unsigned int u = (outer << inner_bit_count) | inner;
- for (unsigned int w = width; w > 0;)
+ unsigned int v = output_map.arrayZ[i];
+ if (v)
{
- p[--w] = u;
- u >>= 8;
+ unsigned int outer = v >> 16;
+ unsigned int inner = v & 0xFFFF;
+ unsigned int u = (outer << inner_bit_count) | inner;
+ for (unsigned int w = width; w > 0;)
+ {
+ p[--w] = u;
+ u >>= 8;
+ }
}
p += width;
}
@@ -242,6 +240,7 @@
/* https://docs.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#tuplevariationheader */
struct TupleVariationHeader
{
+ friend struct tuple_delta_t;
unsigned get_size (unsigned axis_count) const
{ return min_size + get_all_tuples (axis_count).get_size (); }
@@ -250,14 +249,67 @@
const TupleVariationHeader &get_next (unsigned axis_count) const
{ return StructAtOffset<TupleVariationHeader> (this, get_size (axis_count)); }
+ bool unpack_axis_tuples (unsigned axis_count,
+ const hb_array_t<const F2DOT14> shared_tuples,
+ const hb_map_t *axes_old_index_tag_map,
+ hb_hashmap_t<hb_tag_t, Triple>& axis_tuples /* OUT */) const
+ {
+ const F2DOT14 *peak_tuple = nullptr;
+ if (has_peak ())
+ peak_tuple = get_peak_tuple (axis_count).arrayZ;
+ else
+ {
+ unsigned int index = get_index ();
+ if (unlikely ((index + 1) * axis_count > shared_tuples.length))
+ return false;
+ peak_tuple = shared_tuples.sub_array (axis_count * index, axis_count).arrayZ;
+ }
+
+ const F2DOT14 *start_tuple = nullptr;
+ const F2DOT14 *end_tuple = nullptr;
+ bool has_interm = has_intermediate ();
+
+ if (has_interm)
+ {
+ start_tuple = get_start_tuple (axis_count).arrayZ;
+ end_tuple = get_end_tuple (axis_count).arrayZ;
+ }
+
+ for (unsigned i = 0; i < axis_count; i++)
+ {
+ float peak = peak_tuple[i].to_float ();
+ if (peak == 0.f) continue;
+
+ hb_tag_t *axis_tag;
+ if (!axes_old_index_tag_map->has (i, &axis_tag))
+ return false;
+
+ float start, end;
+ if (has_interm)
+ {
+ start = start_tuple[i].to_float ();
+ end = end_tuple[i].to_float ();
+ }
+ else
+ {
+ start = hb_min (peak, 0.f);
+ end = hb_max (peak, 0.f);
+ }
+ axis_tuples.set (*axis_tag, Triple (start, peak, end));
+ }
+
+ return true;
+ }
+
float calculate_scalar (hb_array_t<int> coords, unsigned int coord_count,
const hb_array_t<const F2DOT14> shared_tuples,
- const hb_vector_t<int> *shared_tuple_active_idx = nullptr) const
+ const hb_vector_t<hb_pair_t<int,int>> *shared_tuple_active_idx = nullptr) const
{
const F2DOT14 *peak_tuple;
unsigned start_idx = 0;
unsigned end_idx = coord_count;
+ unsigned step = 1;
if (has_peak ())
peak_tuple = get_peak_tuple (coord_count).arrayZ;
@@ -272,10 +324,16 @@
{
if (unlikely (index >= shared_tuple_active_idx->length))
return 0.f;
- int v = (*shared_tuple_active_idx).arrayZ[index];
- if (v != -1)
+ auto _ = (*shared_tuple_active_idx).arrayZ[index];
+ if (_.second != -1)
{
- start_idx = v;
+ start_idx = _.first;
+ end_idx = _.second + 1;
+ step = _.second - _.first;
+ }
+ else if (_.first != -1)
+ {
+ start_idx = _.first;
end_idx = start_idx + 1;
}
}
@@ -291,7 +349,7 @@
}
float scalar = 1.f;
- for (unsigned int i = start_idx; i < end_idx; i++)
+ for (unsigned int i = start_idx; i < end_idx; i += step)
{
int peak = peak_tuple[i].to_int ();
if (!peak) continue;
@@ -333,6 +391,7 @@
TupleIndexMask = 0x0FFFu
};
+ TuppleIndex& operator = (uint16_t i) { HBUINT16::operator= (i); return *this; }
DEFINE_SIZE_STATIC (2);
};
@@ -365,6 +424,468 @@
DEFINE_SIZE_MIN (4);
};
+/* not using hb_bytes_t: avoid potential build issues with some compilers */
+struct byte_data_t
+{
+ hb_bytes_t bytes;
+
+ byte_data_t () = default;
+ byte_data_t (const char *p_, unsigned len_) : bytes (hb_bytes_t (p_, len_)) {}
+
+ void fini () { bytes.fini (); }
+
+ bool operator == (const byte_data_t& o) const
+ { return bytes.arrayZ == o.bytes.arrayZ && bytes.length == o.bytes.length; }
+
+ explicit operator bool () const { return bytes.length; }
+
+ void copy (hb_serialize_context_t *c) const
+ { c->embed (bytes.arrayZ, bytes.length); }
+};
+
+enum packed_delta_flag_t
+{
+ DELTAS_ARE_ZERO = 0x80,
+ DELTAS_ARE_WORDS = 0x40,
+ DELTA_RUN_COUNT_MASK = 0x3F
+};
+
+struct tuple_delta_t
+{
+ public:
+ hb_hashmap_t<hb_tag_t, Triple> axis_tuples;
+
+ /* indices_length = point_count, indice[i] = 1 means point i is referenced */
+ hb_vector_t<bool> indices;
+
+ hb_vector_t<float> deltas_x;
+ /* empty for cvar tuples */
+ hb_vector_t<float> deltas_y;
+
+ /* compiled data: header and deltas
+ * compiled point data is saved in a hashmap within tuple_variations_t cause
+ * some point sets might be reused by different tuple variations */
+ hb_vector_t<char> compiled_tuple_header;
+ hb_vector_t<char> compiled_deltas;
+
+ tuple_delta_t () = default;
+ tuple_delta_t (const tuple_delta_t& o) = default;
+
+ tuple_delta_t (tuple_delta_t&& o) : tuple_delta_t ()
+ {
+ axis_tuples = std::move (o.axis_tuples);
+ indices = std::move (o.indices);
+ deltas_x = std::move (o.deltas_x);
+ deltas_y = std::move (o.deltas_y);
+ }
+
+ tuple_delta_t& operator = (tuple_delta_t&& o)
+ {
+ hb_swap (*this, o);
+ return *this;
+ }
+
+ void remove_axis (hb_tag_t axis_tag)
+ { axis_tuples.del (axis_tag); }
+
+ bool set_tent (hb_tag_t axis_tag, Triple tent)
+ { return axis_tuples.set (axis_tag, tent); }
+
+ tuple_delta_t& operator += (const tuple_delta_t& o)
+ {
+ unsigned num = indices.length;
+ for (unsigned i = 0; i < num; i++)
+ {
+ if (indices.arrayZ[i])
+ {
+ if (o.indices.arrayZ[i])
+ {
+ deltas_x[i] += o.deltas_x[i];
+ if (deltas_y && o.deltas_y)
+ deltas_y[i] += o.deltas_y[i];
+ }
+ }
+ else
+ {
+ if (!o.indices.arrayZ[i]) continue;
+ deltas_x[i] = o.deltas_x[i];
+ if (deltas_y && o.deltas_y)
+ deltas_y[i] = o.deltas_y[i];
+ }
+ }
+ return *this;
+ }
+
+ tuple_delta_t& operator *= (float scalar)
+ {
+ if (scalar == 1.0f)
+ return *this;
+
+ unsigned num = indices.length;
+ for (unsigned i = 0; i < num; i++)
+ {
+ if (!indices.arrayZ[i]) continue;
+
+ deltas_x[i] *= scalar;
+ if (deltas_y)
+ deltas_y[i] *= scalar;
+ }
+ return *this;
+ }
+
+ hb_vector_t<tuple_delta_t> change_tuple_var_axis_limit (hb_tag_t axis_tag, Triple axis_limit) const
+ {
+ hb_vector_t<tuple_delta_t> out;
+ Triple *tent;
+ if (!axis_tuples.has (axis_tag, &tent))
+ {
+ out.push (*this);
+ return out;
+ }
+
+ if ((tent->minimum < 0.f && tent->maximum > 0.f) ||
+ !(tent->minimum <= tent->middle && tent->middle <= tent->maximum))
+ return out;
+
+ if (tent->middle == 0.f)
+ {
+ out.push (*this);
+ return out;
+ }
+
+ result_t solutions = rebase_tent (*tent, axis_limit);
+ for (auto t : solutions)
+ {
+ tuple_delta_t new_var = *this;
+ if (t.second == Triple ())
+ new_var.remove_axis (axis_tag);
+ else
+ new_var.set_tent (axis_tag, t.second);
+
+ new_var *= t.first;
+ out.push (std::move (new_var));
+ }
+
+ return out;
+ }
+
+ /* deltas should be compiled already before we compile tuple
+ * variation header cause we need to fill in the size of the
+ * serialized data for this tuple variation */
+ //TODO(qxliu):add option to use sharedTuples in gvar
+ bool compile_tuple_var_header (const hb_map_t& axes_index_map,
+ unsigned points_data_length,
+ const hb_map_t& axes_old_index_tag_map)
+ {
+ if (!compiled_deltas) return false;
+
+ unsigned cur_axis_count = axes_index_map.get_population ();
+ /* allocate enough memory: 1 peak + 2 intermediate coords + fixed header size */
+ unsigned alloc_len = 3 * cur_axis_count * (F2DOT14::static_size) + 4;
+ if (unlikely (!compiled_tuple_header.resize (alloc_len))) return false;
+
+ unsigned flag = 0;
+ /* skip the first 4 header bytes: variationDataSize+tupleIndex */
+ F2DOT14* p = reinterpret_cast<F2DOT14 *> (compiled_tuple_header.begin () + 4);
+ F2DOT14* end = reinterpret_cast<F2DOT14 *> (compiled_tuple_header.end ());
+ hb_array_t<F2DOT14> coords (p, end - p);
+
+ /* encode peak coords */
+ unsigned peak_count = encode_peak_coords(coords, flag, axes_index_map, axes_old_index_tag_map);
+ if (!peak_count) return false;
+
+ /* encode interim coords, it's optional so returned num could be 0 */
+ unsigned interim_count = encode_interm_coords (coords.sub_array (peak_count), flag, axes_index_map, axes_old_index_tag_map);
+
+ //TODO(qxliu): add option to use shared_points in gvar
+ flag |= TupleVariationHeader::TuppleIndex::PrivatePointNumbers;
+
+ unsigned serialized_data_size = points_data_length + compiled_deltas.length;
+ TupleVariationHeader *o = reinterpret_cast<TupleVariationHeader *> (compiled_tuple_header.begin ());
+ o->varDataSize = serialized_data_size;
+ o->tupleIndex = flag;
+
+ unsigned total_header_len = 4 + (peak_count + interim_count) * (F2DOT14::static_size);
+ return compiled_tuple_header.resize (total_header_len);
+ }
+
+ unsigned encode_peak_coords (hb_array_t<F2DOT14> peak_coords,
+ unsigned& flag,
+ const hb_map_t& axes_index_map,
+ const hb_map_t& axes_old_index_tag_map) const
+ {
+ unsigned orig_axis_count = axes_old_index_tag_map.get_population ();
+ auto it = peak_coords.iter ();
+ unsigned count = 0;
+ for (unsigned i = 0; i < orig_axis_count; i++)
+ {
+ if (!axes_index_map.has (i)) /* axis pinned */
+ continue;
+ hb_tag_t axis_tag = axes_old_index_tag_map.get (i);
+ Triple *coords;
+ if (!axis_tuples.has (axis_tag, &coords))
+ (*it).set_int (0);
+ else
+ (*it).set_float (coords->middle);
+ it++;
+ count++;
+ }
+ flag |= TupleVariationHeader::TuppleIndex::EmbeddedPeakTuple;
+ return count;
+ }
+
+ /* if no need to encode intermediate coords, then just return p */
+ unsigned encode_interm_coords (hb_array_t<F2DOT14> coords,
+ unsigned& flag,
+ const hb_map_t& axes_index_map,
+ const hb_map_t& axes_old_index_tag_map) const
+ {
+ unsigned orig_axis_count = axes_old_index_tag_map.get_population ();
+ unsigned cur_axis_count = axes_index_map.get_population ();
+
+ auto start_coords_iter = coords.sub_array (0, cur_axis_count).iter ();
+ auto end_coords_iter = coords.sub_array (cur_axis_count).iter ();
+ bool encode_needed = false;
+ unsigned count = 0;
+ for (unsigned i = 0; i < orig_axis_count; i++)
+ {
+ if (!axes_index_map.has (i)) /* axis pinned */
+ continue;
+ hb_tag_t axis_tag = axes_old_index_tag_map.get (i);
+ Triple *coords;
+ float min_val = 0.f, val = 0.f, max_val = 0.f;
+ if (axis_tuples.has (axis_tag, &coords))
+ {
+ min_val = coords->minimum;
+ val = coords->middle;
+ max_val = coords->maximum;
+ }
+
+ (*start_coords_iter).set_float (min_val);
+ (*end_coords_iter).set_float (max_val);
+
+ start_coords_iter++;
+ end_coords_iter++;
+ count += 2;
+ if (min_val != hb_min (val, 0.f) || max_val != hb_max (val, 0.f))
+ encode_needed = true;
+ }
+
+ if (encode_needed)
+ {
+ flag |= TupleVariationHeader::TuppleIndex::IntermediateRegion;
+ return count;
+ }
+ return 0;
+ }
+
+ bool compile_deltas ()
+ {
+ hb_vector_t<int> rounded_deltas;
+ if (unlikely (!rounded_deltas.alloc (indices.length)))
+ return false;
+
+ for (unsigned i = 0; i < indices.length; i++)
+ {
+ if (!indices[i]) continue;
+ int rounded_delta = (int) roundf (deltas_x[i]);
+ rounded_deltas.push (rounded_delta);
+ }
+
+ if (!rounded_deltas) return false;
+ /* allocate enough memories 3 * num_deltas */
+ unsigned alloc_len = 3 * rounded_deltas.length;
+ if (deltas_y)
+ alloc_len *= 2;
+
+ if (unlikely (!compiled_deltas.resize (alloc_len))) return false;
+
+ unsigned i = 0;
+ unsigned encoded_len = encode_delta_run (i, compiled_deltas.as_array (), rounded_deltas);
+
+ if (deltas_y)
+ {
+ /* reuse the rounded_deltas vector, check that deltas_y have the same num of deltas as deltas_x */
+ unsigned j = 0;
+ for (unsigned idx = 0; idx < indices.length; idx++)
+ {
+ if (!indices[idx]) continue;
+ int rounded_delta = (int) roundf (deltas_y[idx]);
+
+ if (j >= rounded_deltas.length) return false;
+
+ rounded_deltas[j++] = rounded_delta;
+ }
+
+ if (j != rounded_deltas.length) return false;
+ encoded_len += encode_delta_run (i, compiled_deltas.as_array ().sub_array (encoded_len), rounded_deltas);
+ }
+ return compiled_deltas.resize (encoded_len);
+ }
+
+ unsigned encode_delta_run (unsigned& i,
+ hb_array_t<char> encoded_bytes,
+ const hb_vector_t<int>& deltas) const
+ {
+ unsigned num_deltas = deltas.length;
+ unsigned encoded_len = 0;
+ while (i < num_deltas)
+ {
+ int val = deltas[i];
+ if (val == 0)
+ encoded_len += encode_delta_run_as_zeroes (i, encoded_bytes.sub_array (encoded_len), deltas);
+ else if (val >= -128 && val <= 127)
+ encoded_len += encode_delta_run_as_bytes (i, encoded_bytes.sub_array (encoded_len), deltas);
+ else
+ encoded_len += encode_delta_run_as_words (i, encoded_bytes.sub_array (encoded_len), deltas);
+ }
+ return encoded_len;
+ }
+
+ unsigned encode_delta_run_as_zeroes (unsigned& i,
+ hb_array_t<char> encoded_bytes,
+ const hb_vector_t<int>& deltas) const
+ {
+ unsigned num_deltas = deltas.length;
+ unsigned run_length = 0;
+ auto it = encoded_bytes.iter ();
+ unsigned encoded_len = 0;
+ while (i < num_deltas && deltas[i] == 0)
+ {
+ i++;
+ run_length++;
+ }
+
+ while (run_length >= 64)
+ {
+ *it++ = char (DELTAS_ARE_ZERO | 63);
+ run_length -= 64;
+ encoded_len++;
+ }
+
+ if (run_length)
+ {
+ *it++ = char (DELTAS_ARE_ZERO | (run_length - 1));
+ encoded_len++;
+ }
+ return encoded_len;
+ }
+
+ unsigned encode_delta_run_as_bytes (unsigned &i,
+ hb_array_t<char> encoded_bytes,
+ const hb_vector_t<int>& deltas) const
+ {
+ unsigned start = i;
+ unsigned num_deltas = deltas.length;
+ while (i < num_deltas)
+ {
+ int val = deltas[i];
+ if (val > 127 || val < -128)
+ break;
+
+ /* from fonttools: if there're 2 or more zeros in a sequence,
+ * it is better to start a new run to save bytes. */
+ if (val == 0 && i + 1 < num_deltas && deltas[i+1] == 0)
+ break;
+
+ i++;
+ }
+ unsigned run_length = i - start;
+
+ unsigned encoded_len = 0;
+ auto it = encoded_bytes.iter ();
+
+ while (run_length >= 64)
+ {
+ *it++ = 63;
+ encoded_len++;
+
+ for (unsigned j = 0; j < 64; j++)
+ {
+ *it++ = static_cast<char> (deltas[start + j]);
+ encoded_len++;
+ }
+
+ start += 64;
+ run_length -= 64;
+ }
+
+ if (run_length)
+ {
+ *it++ = run_length - 1;
+ encoded_len++;
+
+ while (start < i)
+ {
+ *it++ = static_cast<char> (deltas[start++]);
+ encoded_len++;
+ }
+ }
+
+ return encoded_len;
+ }
+
+ unsigned encode_delta_run_as_words (unsigned &i,
+ hb_array_t<char> encoded_bytes,
+ const hb_vector_t<int>& deltas) const
+ {
+ unsigned start = i;
+ unsigned num_deltas = deltas.length;
+ while (i < num_deltas)
+ {
+ int val = deltas[i];
+
+ /* start a new run for a single zero value*/
+ if (val == 0) break;
+
+ /* from fonttools: continue word-encoded run if there's only one
+ * single value in the range [-128, 127] because it is more compact.
+ * Only start a new run when there're 2 continuous such values. */
+ if (val >= -128 && val <= 127 &&
+ i + 1 < num_deltas &&
+ deltas[i+1] >= -128 && deltas[i+1] <= 127)
+ break;
+
+ i++;
+ }
+
+ unsigned run_length = i - start;
+ auto it = encoded_bytes.iter ();
+ unsigned encoded_len = 0;
+ while (run_length >= 64)
+ {
+ *it++ = (DELTAS_ARE_WORDS | 63);
+ encoded_len++;
+
+ for (unsigned j = 0; j < 64; j++)
+ {
+ int16_t delta_val = deltas[start + j];
+ *it++ = static_cast<char> (delta_val >> 8);
+ *it++ = static_cast<char> (delta_val & 0xFF);
+
+ encoded_len += 2;
+ }
+
+ start += 64;
+ run_length -= 64;
+ }
+
+ if (run_length)
+ {
+ *it++ = (DELTAS_ARE_WORDS | (run_length - 1));
+ while (start < i)
+ {
+ int16_t delta_val = deltas[start++];
+ *it++ = static_cast<char> (delta_val >> 8);
+ *it++ = static_cast<char> (delta_val & 0xFF);
+
+ encoded_len += 2;
+ }
+ }
+ return encoded_len;
+ }
+};
+
struct TupleVariationData
{
bool sanitize (hb_sanitize_context_t *c) const
@@ -378,7 +899,7 @@
unsigned get_size (unsigned axis_count) const
{
unsigned total_size = min_size;
- unsigned count = tupleVarCount;
+ unsigned count = tupleVarCount.get_count ();
const TupleVariationHeader *tuple_var_header = &(get_tuple_var_header());
for (unsigned i = 0; i < count; i++)
{
@@ -392,8 +913,354 @@
const TupleVariationHeader &get_tuple_var_header (void) const
{ return StructAfter<TupleVariationHeader> (data); }
+ struct tuple_iterator_t;
+ struct tuple_variations_t
+ {
+ hb_vector_t<tuple_delta_t> tuple_vars;
+
+ private:
+ /* referenced point set->compiled point data map */
+ hb_hashmap_t<const hb_vector_t<bool>*, byte_data_t> point_data_map;
+ /* referenced point set-> count map, used in finding shared points */
+ hb_hashmap_t<const hb_vector_t<bool>*, unsigned> point_set_count_map;
+
+ public:
+ ~tuple_variations_t () { fini (); }
+ void fini ()
+ {
+ for (auto _ : point_data_map.values ())
+ _.fini ();
+
+ point_set_count_map.fini ();
+ tuple_vars.fini ();
+ }
+
+ unsigned get_var_count () const
+ { return tuple_vars.length; }
+
+ bool create_from_tuple_var_data (tuple_iterator_t iterator,
+ unsigned tuple_var_count,
+ unsigned point_count,
+ bool is_gvar,
+ const hb_map_t *axes_old_index_tag_map,
+ const hb_vector_t<unsigned> &shared_indices,
+ const hb_array_t<const F2DOT14> shared_tuples)
+ {
+ do
+ {
+ const HBUINT8 *p = iterator.get_serialized_data ();
+ unsigned int length = iterator.current_tuple->get_data_size ();
+ if (unlikely (!iterator.var_data_bytes.check_range (p, length)))
+ { fini (); return false; }
+
+ hb_hashmap_t<hb_tag_t, Triple> axis_tuples;
+ if (!iterator.current_tuple->unpack_axis_tuples (iterator.get_axis_count (), shared_tuples, axes_old_index_tag_map, axis_tuples)
+ || axis_tuples.is_empty ())
+ { fini (); return false; }
+
+ hb_vector_t<unsigned> private_indices;
+ bool has_private_points = iterator.current_tuple->has_private_points ();
+ const HBUINT8 *end = p + length;
+ if (has_private_points &&
+ !TupleVariationData::unpack_points (p, private_indices, end))
+ { fini (); return false; }
+
+ const hb_vector_t<unsigned> &indices = has_private_points ? private_indices : shared_indices;
+ bool apply_to_all = (indices.length == 0);
+ unsigned num_deltas = apply_to_all ? point_count : indices.length;
+
+ hb_vector_t<int> deltas_x;
+
+ if (unlikely (!deltas_x.resize (num_deltas, false) ||
+ !TupleVariationData::unpack_deltas (p, deltas_x, end)))
+ { fini (); return false; }
+
+ hb_vector_t<int> deltas_y;
+ if (is_gvar)
+ {
+ if (unlikely (!deltas_y.resize (num_deltas, false) ||
+ !TupleVariationData::unpack_deltas (p, deltas_y, end)))
+ { fini (); return false; }
+ }
+
+ tuple_delta_t var;
+ var.axis_tuples = std::move (axis_tuples);
+ if (unlikely (!var.indices.resize (point_count) ||
+ !var.deltas_x.resize (point_count, false)))
+ { fini (); return false; }
+
+ if (is_gvar && unlikely (!var.deltas_y.resize (point_count, false)))
+ { fini (); return false; }
+
+ for (unsigned i = 0; i < num_deltas; i++)
+ {
+ unsigned idx = apply_to_all ? i : indices[i];
+ if (idx >= point_count) continue;
+ var.indices[idx] = true;
+ var.deltas_x[idx] = static_cast<float> (deltas_x[i]);
+ if (is_gvar)
+ var.deltas_y[idx] = static_cast<float> (deltas_y[i]);
+ }
+ tuple_vars.push (std::move (var));
+ } while (iterator.move_to_next ());
+ return true;
+ }
+
+ void change_tuple_variations_axis_limits (const hb_hashmap_t<hb_tag_t, Triple> *normalized_axes_location)
+ {
+ for (auto _ : *normalized_axes_location)
+ {
+ hb_tag_t axis_tag = _.first;
+ Triple axis_limit = _.second;
+ hb_vector_t<tuple_delta_t> new_vars;
+ for (const tuple_delta_t& var : tuple_vars)
+ {
+ hb_vector_t<tuple_delta_t> out = var.change_tuple_var_axis_limit (axis_tag, axis_limit);
+ if (!out) continue;
+ unsigned new_len = new_vars.length + out.length;
+
+ if (unlikely (!new_vars.alloc (new_len, false)))
+ { fini (); return;}
+
+ for (unsigned i = 0; i < out.length; i++)
+ new_vars.push (std::move (out[i]));
+ }
+ tuple_vars.fini ();
+ tuple_vars = std::move (new_vars);
+ }
+ }
+
+ /* merge tuple variations with overlapping tents */
+ void merge_tuple_variations ()
+ {
+ hb_vector_t<tuple_delta_t> new_vars;
+ hb_hashmap_t<hb_hashmap_t<hb_tag_t, Triple>, unsigned> m;
+ unsigned i = 0;
+ for (const tuple_delta_t& var : tuple_vars)
+ {
+ /* if all axes are pinned, drop the tuple variation */
+ if (var.axis_tuples.is_empty ()) continue;
+
+ unsigned *idx;
+ if (m.has (var.axis_tuples, &idx))
+ {
+ new_vars[*idx] += var;
+ }
+ else
+ {
+ new_vars.push (var);
+ m.set (var.axis_tuples, i);
+ i++;
+ }
+ }
+ tuple_vars.fini ();
+ tuple_vars = std::move (new_vars);
+ }
+
+ byte_data_t compile_point_set (const hb_vector_t<bool> &point_indices)
+ {
+ unsigned num_points = 0;
+ for (bool i : point_indices)
+ if (i) num_points++;
+
+ unsigned indices_length = point_indices.length;
+ /* If the points set consists of all points in the glyph, it's encoded with a
+ * single zero byte */
+ if (num_points == indices_length)
+ {
+ char *p = (char *) hb_calloc (1, sizeof (char));
+ if (unlikely (!p)) return byte_data_t ();
+
+ return byte_data_t (p, 1);
+ }
+
+ /* allocate enough memories: 2 bytes for count + 3 bytes for each point */
+ unsigned num_bytes = 2 + 3 *num_points;
+ char *p = (char *) hb_calloc (num_bytes, sizeof (char));
+ if (unlikely (!p)) return byte_data_t ();
+
+ unsigned pos = 0;
+ /* binary data starts with the total number of reference points */
+ if (num_points < 0x80)
+ p[pos++] = num_points;
+ else
+ {
+ p[pos++] = ((num_points >> 8) | 0x80);
+ p[pos++] = num_points & 0xFF;
+ }
+
+ const unsigned max_run_length = 0x7F;
+ unsigned i = 0;
+ unsigned last_value = 0;
+ unsigned num_encoded = 0;
+ while (i < indices_length && num_encoded < num_points)
+ {
+ unsigned run_length = 0;
+ unsigned header_pos = pos;
+ p[pos++] = 0;
+
+ bool use_byte_encoding = false;
+ bool new_run = true;
+ while (i < indices_length && num_encoded < num_points &&
+ run_length <= max_run_length)
+ {
+ // find out next referenced point index
+ while (i < indices_length && !point_indices[i])
+ i++;
+
+ if (i >= indices_length) break;
+
+ unsigned cur_value = i;
+ unsigned delta = cur_value - last_value;
+
+ if (new_run)
+ {
+ use_byte_encoding = (delta <= 0xFF);
+ new_run = false;
+ }
+
+ if (use_byte_encoding && delta > 0xFF)
+ break;
+
+ if (use_byte_encoding)
+ p[pos++] = delta;
+ else
+ {
+ p[pos++] = delta >> 8;
+ p[pos++] = delta & 0xFF;
+ }
+ i++;
+ last_value = cur_value;
+ run_length++;
+ num_encoded++;
+ }
+
+ if (use_byte_encoding)
+ p[header_pos] = run_length - 1;
+ else
+ p[header_pos] = (run_length - 1) | 0x80;
+ }
+ return byte_data_t (p, pos);
+ }
+
+ /* compile all point set and store byte data in a point_set->byte_data_t hashmap,
+ * also update point_set->count map, which will be used in finding shared
+ * point set*/
+ bool compile_all_point_sets ()
+ {
+ for (const auto& tuple: tuple_vars)
+ {
+ const hb_vector_t<bool>* points_set = &(tuple.indices);
+ if (point_data_map.has (points_set))
+ {
+ unsigned *count;
+ if (unlikely (!point_set_count_map.has (points_set, &count) ||
+ !point_set_count_map.set (points_set, (*count) + 1)))
+ return false;
+ continue;
+ }
+
+ byte_data_t compiled_data = compile_point_set (*points_set);
+ if (unlikely (compiled_data == byte_data_t ()))
+ return false;
+
+ if (!point_data_map.set (points_set, compiled_data) ||
+ !point_set_count_map.set (points_set, 1))
+ return false;
+ }
+ return true;
+ }
+
+ /* find shared points set which saves most bytes */
+ byte_data_t find_shared_points ()
+ {
+ unsigned max_saved_bytes = 0;
+ byte_data_t res{};
+
+ for (const auto& _ : point_data_map.iter ())
+ {
+ const hb_vector_t<bool>* points_set = _.first;
+ unsigned data_length = _.second.bytes.length;
+ unsigned *count;
+ if (unlikely (!point_set_count_map.has (points_set, &count) ||
+ *count <= 1))
+ return byte_data_t ();
+
+ unsigned saved_bytes = data_length * ((*count) -1);
+ if (saved_bytes > max_saved_bytes)
+ {
+ max_saved_bytes = saved_bytes;
+ res = _.second;
+ }
+ }
+ return res;
+ }
+
+ void instantiate (const hb_hashmap_t<hb_tag_t, Triple>& normalized_axes_location)
+ {
+ change_tuple_variations_axis_limits (&normalized_axes_location);
+ merge_tuple_variations ();
+ }
+
+ bool compile_bytes (const hb_map_t& axes_index_map,
+ const hb_map_t& axes_old_index_tag_map)
+ {
+ // compile points set and store data in hashmap
+ if (!compile_all_point_sets ())
+ return false;
+ // compile delta and tuple var header for each tuple variation
+ for (auto& tuple: tuple_vars)
+ {
+ const hb_vector_t<bool>* points_set = &(tuple.indices);
+ byte_data_t *points_data;
+ if (unlikely (!point_data_map.has (points_set, &points_data)))
+ return false;
+
+ if (!tuple.compile_deltas ())
+ return false;
+
+ if (!tuple.compile_tuple_var_header (axes_index_map, points_data->bytes.length, axes_old_index_tag_map))
+ return false;
+ }
+ return true;
+ }
+
+ bool serialize_var_headers (hb_serialize_context_t *c, unsigned& total_header_len) const
+ {
+ TRACE_SERIALIZE (this);
+ for (const auto& tuple: tuple_vars)
+ {
+ byte_data_t compiled_bytes {tuple.compiled_tuple_header.arrayZ, tuple.compiled_tuple_header.length};
+ compiled_bytes.copy (c);
+ if (c->in_error ()) return_trace (false);
+ total_header_len += tuple.compiled_tuple_header.length;
+ }
+ return_trace (true);
+ }
+
+ bool serialize_var_data (hb_serialize_context_t *c) const
+ {
+ TRACE_SERIALIZE (this);
+ for (const auto& tuple: tuple_vars)
+ {
+ const hb_vector_t<bool>* points_set = &(tuple.indices);
+ byte_data_t *point_data;
+ if (!point_data_map.has (points_set, &point_data))
+ return_trace (false);
+
+ point_data->copy (c);
+ byte_data_t compiled_bytes {tuple.compiled_deltas.arrayZ, tuple.compiled_deltas.length};
+ compiled_bytes.copy (c);
+ if (c->in_error ()) return_trace (false);
+ }
+ return_trace (true);
+ }
+ };
+
struct tuple_iterator_t
{
+ unsigned get_axis_count () const { return axis_count; }
+
void init (hb_bytes_t var_data_bytes_, unsigned int axis_count_, const void *table_base_)
{
var_data_bytes = var_data_bytes_;
@@ -517,13 +1384,6 @@
hb_vector_t<int> &deltas /* IN/OUT */,
const HBUINT8 *end)
{
- enum packed_delta_flag_t
- {
- DELTAS_ARE_ZERO = 0x80,
- DELTAS_ARE_WORDS = 0x40,
- DELTA_RUN_COUNT_MASK = 0x3F
- };
-
unsigned i = 0;
unsigned count = deltas.length;
while (i < count)
@@ -561,11 +1421,50 @@
bool has_data () const { return tupleVarCount; }
+ bool decompile_tuple_variations (unsigned point_count,
+ bool is_gvar,
+ tuple_iterator_t iterator,
+ const hb_map_t *axes_old_index_tag_map,
+ const hb_vector_t<unsigned> &shared_indices,
+ const hb_array_t<const F2DOT14> shared_tuples,
+ tuple_variations_t& tuple_variations /* OUT */) const
+ {
+ return tuple_variations.create_from_tuple_var_data (iterator, tupleVarCount,
+ point_count, is_gvar,
+ axes_old_index_tag_map,
+ shared_indices,
+ shared_tuples);
+ }
+
+ bool serialize (hb_serialize_context_t *c,
+ bool is_gvar,
+ tuple_variations_t& tuple_variations) const
+ {
+ TRACE_SERIALIZE (this);
+ auto *out = c->start_embed (this);
+ if (unlikely (!c->extend_min (out))) return_trace (false);
+
+ if (!c->check_assign (out->tupleVarCount, tuple_variations.get_var_count (),
+ HB_SERIALIZE_ERROR_INT_OVERFLOW)) return_trace (false);
+
+ unsigned total_header_len = 0;
+
+ if (!tuple_variations.serialize_var_headers (c, total_header_len))
+ return_trace (false);
+
+ unsigned data_offset = min_size + total_header_len;
+ if (!is_gvar) data_offset += 4;
+ if (!c->check_assign (out->data, data_offset, HB_SERIALIZE_ERROR_INT_OVERFLOW)) return_trace (false);
+
+ return tuple_variations.serialize_var_data (c);
+ }
+
protected:
struct TupleVarCount : HBUINT16
{
bool has_shared_point_numbers () const { return ((*this) & SharedPointNumbers); }
unsigned int get_count () const { return (*this) & CountMask; }
+ TupleVarCount& operator = (uint16_t i) { HBUINT16::operator= (i); return *this; }
protected:
enum Flags
diff --git a/src/hb-ot-var-cvar-table.hh b/src/hb-ot-var-cvar-table.hh
index 7fd0f1d..de54339 100644
--- a/src/hb-ot-var-cvar-table.hh
+++ b/src/hb-ot-var-cvar-table.hh
@@ -27,6 +27,7 @@
#define HB_OT_VAR_CVAR_TABLE_HH
#include "hb-ot-var-common.hh"
+#include "hb-ot-var-fvar-table.hh"
namespace OT {
@@ -51,6 +52,27 @@
const TupleVariationData* get_tuple_var_data (void) const
{ return &tupleVariationData; }
+ bool decompile_tuple_variations (unsigned axis_count,
+ unsigned point_count,
+ bool is_gvar,
+ const hb_map_t *axes_old_index_tag_map,
+ TupleVariationData::tuple_variations_t& tuple_variations /* OUT */) const
+ {
+ hb_vector_t<unsigned> shared_indices;
+ TupleVariationData::tuple_iterator_t iterator;
+ unsigned var_data_length = tupleVariationData.get_size (axis_count);
+ hb_bytes_t var_data_bytes = hb_bytes_t (reinterpret_cast<const char*> (get_tuple_var_data ()), var_data_length);
+ if (!TupleVariationData::get_tuple_iterator (var_data_bytes, axis_count, this,
+ shared_indices, &iterator))
+ return false;
+
+ return tupleVariationData.decompile_tuple_variations (point_count, is_gvar, iterator,
+ axes_old_index_tag_map,
+ shared_indices,
+ hb_array<const F2DOT14> (),
+ tuple_variations);
+ }
+
static bool calculate_cvt_deltas (unsigned axis_count,
hb_array_t<int> coords,
unsigned num_cvt_item,
@@ -104,6 +126,53 @@
return true;
}
+
+ bool serialize (hb_serialize_context_t *c,
+ TupleVariationData::tuple_variations_t& tuple_variations) const
+ {
+ TRACE_SERIALIZE (this);
+ if (unlikely (!c->embed (version))) return_trace (false);
+
+ return_trace (tupleVariationData.serialize (c, false, tuple_variations));
+ }
+
+ bool subset (hb_subset_context_t *c) const
+ {
+ TRACE_SUBSET (this);
+ if (c->plan->all_axes_pinned)
+ return_trace (false);
+
+ /* subset() for cvar is called by partial instancing only, we always pass
+ * through cvar table in other cases */
+ if (!c->plan->normalized_coords)
+ {
+ unsigned axis_count = c->plan->source->table.fvar->get_axis_count ();
+ unsigned total_size = min_size + tupleVariationData.get_size (axis_count);
+ char *out = c->serializer->allocate_size<char> (total_size);
+ if (unlikely (!out)) return_trace (false);
+
+ hb_memcpy (out, this, total_size);
+ return_trace (true);
+ }
+
+ OT::TupleVariationData::tuple_variations_t tuple_variations;
+ unsigned axis_count = c->plan->axes_old_index_tag_map.get_population ();
+
+ const hb_tag_t cvt = HB_TAG('c','v','t',' ');
+ hb_blob_t *cvt_blob = hb_face_reference_table (c->plan->source, cvt);
+ unsigned point_count = hb_blob_get_length (cvt_blob) / FWORD::static_size;
+
+ if (!decompile_tuple_variations (axis_count, point_count, false,
+ &(c->plan->axes_old_index_tag_map),
+ tuple_variations))
+ return_trace (false);
+
+ tuple_variations.instantiate (c->plan->axes_location);
+ if (!tuple_variations.compile_bytes (c->plan->axes_index_map, c->plan->axes_old_index_tag_map))
+ return_trace (false);
+
+ return_trace (serialize (c->serializer, tuple_variations));
+ }
static bool add_cvt_and_apply_deltas (hb_subset_plan_t *plan,
const TupleVariationData *tuple_var_data,
diff --git a/src/hb-ot-var-fvar-table.hh b/src/hb-ot-var-fvar-table.hh
index a384dfa..d8e789c 100644
--- a/src/hb-ot-var-fvar-table.hh
+++ b/src/hb-ot-var-fvar-table.hh
@@ -39,6 +39,24 @@
namespace OT {
+static bool axis_coord_pinned_or_within_axis_range (const hb_array_t<const F16DOT16> coords,
+ unsigned axis_index,
+ Triple axis_limit)
+{
+ float axis_coord = coords[axis_index].to_float ();
+ if (axis_limit.is_point ())
+ {
+ if (axis_limit.minimum != axis_coord)
+ return false;
+ }
+ else
+ {
+ if (axis_coord < axis_limit.minimum ||
+ axis_coord > axis_limit.maximum)
+ return false;
+ }
+ return true;
+}
struct InstanceRecord
{
@@ -47,6 +65,27 @@
hb_array_t<const F16DOT16> get_coordinates (unsigned int axis_count) const
{ return coordinatesZ.as_array (axis_count); }
+ bool keep_instance (unsigned axis_count,
+ const hb_map_t *axes_index_tag_map,
+ const hb_hashmap_t<hb_tag_t, Triple> *axes_location) const
+ {
+ if (axes_location->is_empty ()) return true;
+ const hb_array_t<const F16DOT16> coords = get_coordinates (axis_count);
+ for (unsigned i = 0 ; i < axis_count; i++)
+ {
+ uint32_t *axis_tag;
+ if (!axes_index_tag_map->has (i, &axis_tag))
+ return false;
+ if (!axes_location->has (*axis_tag))
+ continue;
+
+ Triple axis_limit = axes_location->get (*axis_tag);
+ if (!axis_coord_pinned_or_within_axis_range (coords, i, axis_limit))
+ return false;
+ }
+ return true;
+ }
+
bool subset (hb_subset_context_t *c,
unsigned axis_count,
bool has_postscript_nameid) const
@@ -56,19 +95,22 @@
if (unlikely (!c->serializer->embed (flags))) return_trace (false);
const hb_array_t<const F16DOT16> coords = get_coordinates (axis_count);
- const hb_hashmap_t<hb_tag_t, float> *axes_location = &c->plan->user_axes_location;
+ const hb_hashmap_t<hb_tag_t, Triple> *axes_location = &c->plan->user_axes_location;
for (unsigned i = 0 ; i < axis_count; i++)
{
uint32_t *axis_tag;
// only keep instances whose coordinates == pinned axis location
if (!c->plan->axes_old_index_tag_map.has (i, &axis_tag)) continue;
-
- if (axes_location->has (*axis_tag) &&
- fabsf (axes_location->get (*axis_tag) - coords[i].to_float ()) > 0.001f)
- return_trace (false);
-
- if (!c->plan->axes_index_map.has (i))
- continue;
+ if (axes_location->has (*axis_tag))
+ {
+ Triple axis_limit = axes_location->get (*axis_tag);
+ if (!axis_coord_pinned_or_within_axis_range (coords, i, axis_limit))
+ return_trace (false);
+
+ //skip pinned axis
+ if (axis_limit.is_point ())
+ continue;
+ }
if (!c->serializer->embed (coords[i]))
return_trace (false);
@@ -216,7 +258,8 @@
axisSize == 20 && /* Assumed in our code. */
instanceSize >= axisCount * 4 + 4 &&
get_axes ().sanitize (c) &&
- c->check_range (get_instance (0), instanceCount, instanceSize));
+ c->check_range (&StructAfter<InstanceRecord> (get_axes ()),
+ instanceCount, instanceSize));
}
unsigned int get_axis_count () const { return axisCount; }
@@ -314,21 +357,19 @@
return axisCount;
}
- void collect_name_ids (hb_hashmap_t<hb_tag_t, float> *user_axes_location,
+ void collect_name_ids (hb_hashmap_t<hb_tag_t, Triple> *user_axes_location,
+ hb_map_t *axes_old_index_tag_map,
hb_set_t *nameids /* IN/OUT */) const
{
if (!has_data ()) return;
- hb_map_t pinned_axes;
auto axis_records = get_axes ();
for (unsigned i = 0 ; i < (unsigned)axisCount; i++)
{
hb_tag_t axis_tag = axis_records[i].get_axis_tag ();
- if (user_axes_location->has (axis_tag))
- {
- pinned_axes.set (i, axis_tag);
+ if (user_axes_location->has (axis_tag) &&
+ user_axes_location->get (axis_tag).is_point ())
continue;
- }
nameids->add (axis_records[i].get_name_id ());
}
@@ -337,16 +378,7 @@
{
const InstanceRecord *instance = get_instance (i);
- if (hb_any (+ hb_enumerate (instance->get_coordinates (axisCount))
- | hb_filter (pinned_axes, hb_first)
- | hb_map ([&] (const hb_pair_t<unsigned, const F16DOT16&>& _)
- {
- hb_tag_t axis_tag = pinned_axes.get (_.first);
- float location = user_axes_location->get (axis_tag);
- if (fabs ((double)location - (double)_.second.to_float ()) > 0.001) return true;
- return false;
- })
- ))
+ if (!instance->keep_instance (axisCount, axes_old_index_tag_map, user_axes_location))
continue;
nameids->add (instance->subfamilyNameID);
diff --git a/src/hb-ot-var-gvar-table.hh b/src/hb-ot-var-gvar-table.hh
index ece892e..b5099ac 100644
--- a/src/hb-ot-var-gvar-table.hh
+++ b/src/hb-ot-var-gvar-table.hh
@@ -50,13 +50,14 @@
y = x * matrix[1] + y * matrix[3];
x = x_;
}
+ HB_ALWAYS_INLINE
void translate (const contour_point_t &p) { x += p.x; y += p.y; }
- float x = 0.f;
- float y = 0.f;
- uint8_t flag = 0;
- bool is_end_point = false;
+ float x;
+ float y;
+ uint8_t flag;
+ bool is_end_point;
};
struct contour_point_vector_t : hb_vector_t<contour_point_t>
@@ -110,20 +111,20 @@
unsigned int num_glyphs = c->plan->num_output_glyphs ();
out->glyphCountX = hb_min (0xFFFFu, num_glyphs);
+ auto it = hb_iter (c->plan->new_to_old_gid_list);
+ if (it->first == 0 && !(c->plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE))
+ it++;
unsigned int subset_data_size = 0;
- for (hb_codepoint_t gid = (c->plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE) ? 0 : 1;
- gid < num_glyphs;
- gid++)
+ for (auto &_ : it)
{
- hb_codepoint_t old_gid;
- if (!c->plan->old_gid_for_new_gid (gid, &old_gid)) continue;
+ hb_codepoint_t old_gid = _.second;
subset_data_size += get_glyph_var_data_bytes (c->source_blob, glyph_count, old_gid).length;
}
bool long_offset = subset_data_size & ~0xFFFFu;
out->flags = long_offset ? 1 : 0;
- HBUINT8 *subset_offsets = c->serializer->allocate_size<HBUINT8> ((long_offset ? 4 : 2) * (num_glyphs + 1));
+ HBUINT8 *subset_offsets = c->serializer->allocate_size<HBUINT8> ((long_offset ? 4 : 2) * (num_glyphs + 1), false);
if (!subset_offsets) return_trace (false);
/* shared tuples */
@@ -138,36 +139,61 @@
hb_memcpy (tuples, this+sharedTuples, shared_tuple_size);
}
- char *subset_data = c->serializer->allocate_size<char> (subset_data_size);
+ char *subset_data = c->serializer->allocate_size<char> (subset_data_size, false);
if (!subset_data) return_trace (false);
out->dataZ = subset_data - (char *) out;
- unsigned int glyph_offset = 0;
- for (hb_codepoint_t gid = (c->plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE) ? 0 : 1;
- gid < num_glyphs;
- gid++)
+
+ if (long_offset)
{
- hb_codepoint_t old_gid;
- hb_bytes_t var_data_bytes = c->plan->old_gid_for_new_gid (gid, &old_gid)
- ? get_glyph_var_data_bytes (c->source_blob,
+ ((HBUINT32 *) subset_offsets)[0] = 0;
+ subset_offsets += 4;
+ }
+ else
+ {
+ ((HBUINT16 *) subset_offsets)[0] = 0;
+ subset_offsets += 2;
+ }
+ unsigned int glyph_offset = 0;
+
+ hb_codepoint_t last = 0;
+ it = hb_iter (c->plan->new_to_old_gid_list);
+ if (it->first == 0 && !(c->plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE))
+ it++;
+ for (auto &_ : it)
+ {
+ hb_codepoint_t gid = _.first;
+ hb_codepoint_t old_gid = _.second;
+
+ if (long_offset)
+ for (; last < gid; last++)
+ ((HBUINT32 *) subset_offsets)[last] = glyph_offset;
+ else
+ for (; last < gid; last++)
+ ((HBUINT16 *) subset_offsets)[last] = glyph_offset / 2;
+
+ hb_bytes_t var_data_bytes = get_glyph_var_data_bytes (c->source_blob,
glyph_count,
- old_gid)
- : hb_bytes_t ();
+ old_gid);
+
+ hb_memcpy (subset_data, var_data_bytes.arrayZ, var_data_bytes.length);
+ subset_data += var_data_bytes.length;
+ glyph_offset += var_data_bytes.length;
if (long_offset)
((HBUINT32 *) subset_offsets)[gid] = glyph_offset;
else
((HBUINT16 *) subset_offsets)[gid] = glyph_offset / 2;
- if (var_data_bytes.length > 0)
- hb_memcpy (subset_data, var_data_bytes.arrayZ, var_data_bytes.length);
- subset_data += var_data_bytes.length;
- glyph_offset += var_data_bytes.length;
+ last++; // Skip over gid
}
+
if (long_offset)
- ((HBUINT32 *) subset_offsets)[num_glyphs] = glyph_offset;
+ for (; last < num_glyphs; last++)
+ ((HBUINT32 *) subset_offsets)[last] = glyph_offset;
else
- ((HBUINT16 *) subset_offsets)[num_glyphs] = glyph_offset / 2;
+ for (; last < num_glyphs; last++)
+ ((HBUINT16 *) subset_offsets)[last] = glyph_offset / 2;
return_trace (true);
}
@@ -216,21 +242,24 @@
for (unsigned i = 0; i < count; i++)
{
hb_array_t<const F2DOT14> tuple = shared_tuples.sub_array (axis_count * i, axis_count);
- int idx = -1;
+ int idx1 = -1, idx2 = -1;
for (unsigned j = 0; j < axis_count; j++)
{
const F2DOT14 &peak = tuple.arrayZ[j];
if (peak.to_int () != 0)
{
- if (idx != -1)
+ if (idx1 == -1)
+ idx1 = j;
+ else if (idx2 == -1)
+ idx2 = j;
+ else
{
- idx = -1;
+ idx1 = idx2 = -1;
break;
}
- idx = j;
}
}
- shared_tuple_active_idx.arrayZ[i] = idx;
+ shared_tuple_active_idx.arrayZ[i] = {idx1, idx2};
}
}
~accelerator_t () { table.destroy (); }
@@ -266,10 +295,9 @@
public:
bool apply_deltas_to_points (hb_codepoint_t glyph,
hb_array_t<int> coords,
- const hb_array_t<contour_point_t> points) const
+ const hb_array_t<contour_point_t> points,
+ bool phantom_only = false) const
{
- if (!coords) return true;
-
if (unlikely (glyph >= glyphCount)) return true;
hb_bytes_t var_data_bytes = table->get_glyph_var_data_bytes (table.get_blob (), glyphCount, glyph);
@@ -297,6 +325,7 @@
hb_vector_t<unsigned int> private_indices;
hb_vector_t<int> x_deltas;
hb_vector_t<int> y_deltas;
+ unsigned count = points.length;
bool flush = false;
do
{
@@ -310,9 +339,10 @@
if (!deltas)
{
- if (unlikely (!deltas_vec.resize (points.length, false))) return false;
+ if (unlikely (!deltas_vec.resize (count, false))) return false;
deltas = deltas_vec.as_array ();
- hb_memset (deltas.arrayZ, 0, deltas.get_size ()); // Faster than vector resize
+ hb_memset (deltas.arrayZ + (phantom_only ? count - 4 : 0), 0,
+ (phantom_only ? 4 : count) * sizeof (deltas[0]));
}
const HBUINT8 *end = p + length;
@@ -332,7 +362,7 @@
if (!apply_to_all)
{
- if (!orig_points)
+ if (!orig_points && !phantom_only)
{
orig_points_vec.extend (points);
if (unlikely (orig_points_vec.in_error ())) return false;
@@ -341,13 +371,13 @@
if (flush)
{
- unsigned count = points.length;
- for (unsigned int i = 0; i < count; i++)
+ for (unsigned int i = phantom_only ? count - 4 : 0; i < count; i++)
points.arrayZ[i].translate (deltas.arrayZ[i]);
flush = false;
}
- hb_memset (deltas.arrayZ, 0, deltas.get_size ());
+ hb_memset (deltas.arrayZ + (phantom_only ? count - 4 : 0), 0,
+ (phantom_only ? 4 : count) * sizeof (deltas[0]));
}
if (HB_OPTIMIZE_SIZE_VAL)
@@ -362,6 +392,7 @@
pt_index = indices[i];
if (unlikely (pt_index >= deltas.length)) continue;
}
+ if (phantom_only && pt_index < count - 4) continue;
auto &delta = deltas.arrayZ[pt_index];
delta.flag = 1; /* this point is referenced, i.e., explicit deltas specified */
delta.x += x_deltas.arrayZ[i] * scalar;
@@ -374,7 +405,7 @@
if (scalar != 1.0f)
{
if (apply_to_all)
- for (unsigned int i = 0; i < num_deltas; i++)
+ for (unsigned int i = phantom_only ? count - 4 : 0; i < count; i++)
{
unsigned int pt_index = i;
auto &delta = deltas.arrayZ[pt_index];
@@ -386,6 +417,7 @@
{
unsigned int pt_index = indices[i];
if (unlikely (pt_index >= deltas.length)) continue;
+ if (phantom_only && pt_index < count - 4) continue;
auto &delta = deltas.arrayZ[pt_index];
delta.flag = 1; /* this point is referenced, i.e., explicit deltas specified */
delta.x += x_deltas.arrayZ[i] * scalar;
@@ -395,7 +427,7 @@
else
{
if (apply_to_all)
- for (unsigned int i = 0; i < num_deltas; i++)
+ for (unsigned int i = phantom_only ? count - 4 : 0; i < count; i++)
{
unsigned int pt_index = i;
auto &delta = deltas.arrayZ[pt_index];
@@ -407,6 +439,7 @@
{
unsigned int pt_index = indices[i];
if (unlikely (pt_index >= deltas.length)) continue;
+ if (phantom_only && pt_index < count - 4) continue;
auto &delta = deltas.arrayZ[pt_index];
delta.flag = 1; /* this point is referenced, i.e., explicit deltas specified */
delta.x += x_deltas.arrayZ[i];
@@ -416,11 +449,10 @@
}
/* infer deltas for unreferenced points */
- if (!apply_to_all)
+ if (!apply_to_all && !phantom_only)
{
if (!end_points)
{
- unsigned count = points.length;
for (unsigned i = 0; i < count; ++i)
if (points.arrayZ[i].is_end_point)
end_points.push (i);
@@ -482,8 +514,7 @@
if (flush)
{
- unsigned count = points.length;
- for (unsigned int i = 0; i < count; i++)
+ for (unsigned int i = phantom_only ? count - 4 : 0; i < count; i++)
points.arrayZ[i].translate (deltas.arrayZ[i]);
}
@@ -495,7 +526,7 @@
private:
hb_blob_ptr_t<gvar> table;
unsigned glyphCount;
- hb_vector_t<signed> shared_tuple_active_idx;
+ hb_vector_t<hb_pair_t<int, int>> shared_tuple_active_idx;
};
protected:
diff --git a/src/hb-ot-var-hvar-table.hh b/src/hb-ot-var-hvar-table.hh
index 461b2b9..490f883 100644
--- a/src/hb-ot-var-hvar-table.hh
+++ b/src/hb-ot-var-hvar-table.hh
@@ -45,7 +45,8 @@
void init (const DeltaSetIndexMap &index_map,
hb_inc_bimap_t &outer_map,
hb_vector_t<hb_set_t *> &inner_sets,
- const hb_subset_plan_t *plan)
+ const hb_subset_plan_t *plan,
+ bool bypass_empty = true)
{
map_count = 0;
outer_bit_count = 0;
@@ -53,55 +54,51 @@
max_inners.init ();
output_map.init ();
- if (&index_map == &Null (DeltaSetIndexMap)) return;
+ if (bypass_empty && !index_map.get_map_count ()) return;
unsigned int last_val = (unsigned int)-1;
- hb_codepoint_t last_gid = (hb_codepoint_t)-1;
- hb_codepoint_t gid = (hb_codepoint_t) hb_min (index_map.get_map_count (), plan->num_output_glyphs ());
+ hb_codepoint_t last_gid = HB_CODEPOINT_INVALID;
outer_bit_count = (index_map.get_width () * 8) - index_map.get_inner_bit_count ();
max_inners.resize (inner_sets.length);
for (unsigned i = 0; i < inner_sets.length; i++) max_inners[i] = 0;
/* Search backwards for a map value different from the last map value */
- for (; gid > 0; gid--)
+ auto &new_to_old_gid_list = plan->new_to_old_gid_list;
+ unsigned count = new_to_old_gid_list.length;
+ for (unsigned j = count; j; j--)
{
- hb_codepoint_t old_gid;
- if (!plan->old_gid_for_new_gid (gid - 1, &old_gid))
- {
- if (last_gid == (hb_codepoint_t) -1)
- continue;
- else
- break;
- }
+ hb_codepoint_t gid = new_to_old_gid_list.arrayZ[j - 1].first;
+ hb_codepoint_t old_gid = new_to_old_gid_list.arrayZ[j - 1].second;
unsigned int v = index_map.map (old_gid);
- if (last_gid == (hb_codepoint_t) -1)
+ if (last_gid == HB_CODEPOINT_INVALID)
{
last_val = v;
last_gid = gid;
continue;
}
- if (v != last_val) break;
+ if (v != last_val)
+ break;
last_gid = gid;
}
if (unlikely (last_gid == (hb_codepoint_t)-1)) return;
- map_count = last_gid;
- for (gid = 0; gid < map_count; gid++)
+ map_count = last_gid + 1;
+ for (auto _ : plan->new_to_old_gid_list)
{
- hb_codepoint_t old_gid;
- if (plan->old_gid_for_new_gid (gid, &old_gid))
- {
- unsigned int v = index_map.map (old_gid);
- unsigned int outer = v >> 16;
- unsigned int inner = v & 0xFFFF;
- outer_map.add (outer);
- if (inner > max_inners[outer]) max_inners[outer] = inner;
- if (outer >= inner_sets.length) return;
- inner_sets[outer]->add (inner);
- }
+ hb_codepoint_t gid = _.first;
+ if (gid >= map_count) break;
+
+ hb_codepoint_t old_gid = _.second;
+ unsigned int v = index_map.map (old_gid);
+ unsigned int outer = v >> 16;
+ unsigned int inner = v & 0xFFFF;
+ outer_map.add (outer);
+ if (inner > max_inners[outer]) max_inners[outer] = inner;
+ if (outer >= inner_sets.length) return;
+ inner_sets[outer]->add (inner);
}
}
@@ -116,8 +113,6 @@
const hb_vector_t<hb_inc_bimap_t> &inner_maps,
const hb_subset_plan_t *plan)
{
- if (input_map == &Null (DeltaSetIndexMap)) return;
-
for (unsigned int i = 0; i < max_inners.length; i++)
{
if (inner_maps[i].get_population () == 0) continue;
@@ -125,18 +120,17 @@
if (bit_count > inner_bit_count) inner_bit_count = bit_count;
}
- output_map.resize (map_count);
- for (hb_codepoint_t gid = 0; gid < output_map.length; gid++)
+ if (unlikely (!output_map.resize (map_count))) return;
+ for (const auto &_ : plan->new_to_old_gid_list)
{
- hb_codepoint_t old_gid;
- if (plan->old_gid_for_new_gid (gid, &old_gid))
- {
- uint32_t v = input_map->map (old_gid);
- unsigned int outer = v >> 16;
- output_map[gid] = (outer_map[outer] << 16) | (inner_maps[outer][v & 0xFFFF]);
- }
- else
- output_map[gid] = 0; /* Map unused glyph to outer/inner=0/0 */
+ hb_codepoint_t new_gid = _.first;
+ hb_codepoint_t old_gid = _.second;
+
+ if (unlikely (new_gid >= map_count)) break;
+
+ uint32_t v = input_map->map (old_gid);
+ unsigned int outer = v >> 16;
+ output_map.arrayZ[new_gid] = (outer_map[outer] << 16) | (inner_maps[outer][v & 0xFFFF]);
}
}
@@ -180,7 +174,7 @@
if (unlikely (!index_map_plans.length || !inner_sets.length || !inner_maps.length)) return;
bool retain_adv_map = false;
- index_map_plans[0].init (*index_maps[0], outer_map, inner_sets, plan);
+ index_map_plans[0].init (*index_maps[0], outer_map, inner_sets, plan, false);
if (index_maps[0] == &Null (DeltaSetIndexMap))
{
retain_adv_map = plan->flags & HB_SUBSET_FLAGS_RETAIN_GIDS;
@@ -197,12 +191,10 @@
if (retain_adv_map)
{
- for (hb_codepoint_t gid = 0; gid < plan->num_output_glyphs (); gid++)
+ for (const auto &_ : plan->new_to_old_gid_list)
{
- if (inner_sets[0]->has (gid))
- inner_maps[0].add (gid);
- else
- inner_maps[0].skip ();
+ hb_codepoint_t old_gid = _.second;
+ inner_maps[0].add (old_gid);
}
}
else
diff --git a/src/hb-ot-vorg-table.hh b/src/hb-ot-vorg-table.hh
index 811e139..671b6d2 100644
--- a/src/hb-ot-vorg-table.hh
+++ b/src/hb-ot-vorg-table.hh
@@ -90,7 +90,7 @@
bool subset (hb_subset_context_t *c) const
{
TRACE_SUBSET (this);
- VORG *vorg_prime = c->serializer->start_embed<VORG> ();
+ auto *vorg_prime = c->serializer->start_embed<VORG> ();
if (unlikely (!c->serializer->check_success (vorg_prime))) return_trace (false);
auto it =
diff --git a/src/hb-pool.hh b/src/hb-pool.hh
index d6eb778..fcf1066 100644
--- a/src/hb-pool.hh
+++ b/src/hb-pool.hh
@@ -58,7 +58,7 @@
if (unlikely (!next))
{
if (unlikely (!chunks.alloc (chunks.length + 1))) return nullptr;
- chunk_t *chunk = (chunk_t *) hb_calloc (1, sizeof (chunk_t));
+ chunk_t *chunk = (chunk_t *) hb_malloc (sizeof (chunk_t));
if (unlikely (!chunk)) return nullptr;
chunks.push (chunk);
next = chunk->thread ();
diff --git a/src/hb-priority-queue.hh b/src/hb-priority-queue.hh
index bf1b282..938c028 100644
--- a/src/hb-priority-queue.hh
+++ b/src/hb-priority-queue.hh
@@ -61,6 +61,9 @@
bubble_up (heap.length - 1);
}
+#ifndef HB_OPTIMIZE_SIZE
+ HB_ALWAYS_INLINE
+#endif
item_t pop_minimum ()
{
assert (!is_empty ());
@@ -106,8 +109,10 @@
return 2 * index + 2;
}
+ HB_ALWAYS_INLINE
void bubble_down (unsigned index)
{
+ repeat:
assert (index < heap.length);
unsigned left = left_child (index);
@@ -123,19 +128,20 @@
&& (!has_right || heap.arrayZ[index].first <= heap.arrayZ[right].first))
return;
+ unsigned child;
if (!has_right || heap.arrayZ[left].first < heap.arrayZ[right].first)
- {
- swap (index, left);
- bubble_down (left);
- return;
- }
+ child = left;
+ else
+ child = right;
- swap (index, right);
- bubble_down (right);
+ swap (index, child);
+ index = child;
+ goto repeat;
}
void bubble_up (unsigned index)
{
+ repeat:
assert (index < heap.length);
if (index == 0) return;
@@ -145,7 +151,8 @@
return;
swap (index, parent_index);
- bubble_up (parent_index);
+ index = parent_index;
+ goto repeat;
}
void swap (unsigned a, unsigned b)
diff --git a/src/hb-sanitize.hh b/src/hb-sanitize.hh
index 5259891..2d338c5 100644
--- a/src/hb-sanitize.hh
+++ b/src/hb-sanitize.hh
@@ -122,12 +122,14 @@
{
hb_sanitize_context_t () :
start (nullptr), end (nullptr),
+ length (0),
max_ops (0), max_subtables (0),
recursion_depth (0),
writable (false), edit_count (0),
blob (nullptr),
num_glyphs (65536),
- num_glyphs_set (false) {}
+ num_glyphs_set (false),
+ lazy_some_gpos (false) {}
const char *get_name () { return "SANITIZE"; }
template <typename T, typename F>
@@ -155,6 +157,19 @@
dispatch (const T &obj, Ts&&... ds) HB_AUTO_RETURN
( _dispatch (obj, hb_prioritize, std::forward<Ts> (ds)...) )
+ hb_sanitize_context_t (hb_blob_t *b) : hb_sanitize_context_t ()
+ {
+ init (b);
+
+ if (blob)
+ start_processing ();
+ }
+
+ ~hb_sanitize_context_t ()
+ {
+ if (blob)
+ end_processing ();
+ }
void init (hb_blob_t *b)
{
@@ -180,11 +195,15 @@
const char *obj_start = (const char *) obj;
if (unlikely (obj_start < this->start || this->end <= obj_start))
+ {
this->start = this->end = nullptr;
+ this->length = 0;
+ }
else
{
this->start = obj_start;
this->end = obj_start + hb_min (size_t (this->end - obj_start), obj->get_size ());
+ this->length = this->end - this->start;
}
}
@@ -192,6 +211,7 @@
{
this->start = this->blob->data;
this->end = this->start + this->blob->length;
+ this->length = this->end - this->start;
assert (this->start <= this->end); /* Must not overflow. */
}
@@ -224,6 +244,7 @@
hb_blob_destroy (this->blob);
this->blob = nullptr;
this->start = this->end = nullptr;
+ this->length = 0;
}
unsigned get_edit_count () { return edit_count; }
@@ -240,15 +261,16 @@
return (this->max_ops -= (int) count) > 0;
}
+#ifndef HB_OPTIMIZE_SIZE
+ HB_ALWAYS_INLINE
+#endif
bool check_range (const void *base,
unsigned int len) const
{
const char *p = (const char *) base;
- bool ok = !len ||
- (this->start <= p &&
- p <= this->end &&
- (unsigned int) (this->end - p) >= len &&
- (this->max_ops -= len) > 0);
+ bool ok = (uintptr_t) (p - this->start) <= this->length &&
+ (unsigned int) (this->end - p) >= len &&
+ ((this->max_ops -= len) > 0);
DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0,
"check_range [%p..%p]"
@@ -259,6 +281,43 @@
return likely (ok);
}
+#ifndef HB_OPTIMIZE_SIZE
+ HB_ALWAYS_INLINE
+#endif
+ bool check_range_fast (const void *base,
+ unsigned int len) const
+ {
+ const char *p = (const char *) base;
+ bool ok = ((uintptr_t) (p - this->start) <= this->length &&
+ (unsigned int) (this->end - p) >= len);
+
+ DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0,
+ "check_range_fast [%p..%p]"
+ " (%u bytes) in [%p..%p] -> %s",
+ p, p + len, len,
+ this->start, this->end,
+ ok ? "OK" : "OUT-OF-RANGE");
+
+ return likely (ok);
+ }
+
+#ifndef HB_OPTIMIZE_SIZE
+ HB_ALWAYS_INLINE
+#endif
+ bool check_point (const void *base) const
+ {
+ const char *p = (const char *) base;
+ bool ok = (uintptr_t) (p - this->start) <= this->length;
+
+ DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0,
+ "check_point [%p]"
+ " in [%p..%p] -> %s",
+ p,
+ this->start, this->end,
+ ok ? "OK" : "OUT-OF-RANGE");
+
+ return likely (ok);
+ }
template <typename T>
bool check_range (const T *base,
@@ -282,6 +341,20 @@
}
template <typename T>
+ HB_ALWAYS_INLINE
+ bool check_array_sized (const T *base, unsigned int len, unsigned len_size) const
+ {
+ if (len_size >= 4)
+ {
+ if (unlikely (hb_unsigned_mul_overflows (len, hb_static_size (T), &len)))
+ return false;
+ }
+ else
+ len = len * hb_static_size (T);
+ return this->check_range (base, len);
+ }
+
+ template <typename T>
bool check_array (const T *base, unsigned int len) const
{
return this->check_range (base, len, hb_static_size (T));
@@ -292,7 +365,7 @@
unsigned int a,
unsigned int b) const
{
- return this->check_range (base, a, b, hb_static_size (T));
+ return this->check_range (base, hb_static_size (T), a, b);
}
bool check_start_recursion (int max_depth)
@@ -309,7 +382,12 @@
template <typename Type>
bool check_struct (const Type *obj) const
- { return likely (this->check_range (obj, obj->min_size)); }
+ {
+ if (sizeof (uintptr_t) == sizeof (uint32_t))
+ return likely (this->check_range_fast (obj, obj->min_size));
+ else
+ return likely (this->check_point ((const char *) obj + obj->min_size));
+ }
bool may_edit (const void *base, unsigned int len)
{
@@ -416,6 +494,7 @@
}
const char *start, *end;
+ unsigned length;
mutable int max_ops, max_subtables;
private:
int recursion_depth;
@@ -424,6 +503,8 @@
hb_blob_t *blob;
unsigned int num_glyphs;
bool num_glyphs_set;
+ public:
+ bool lazy_some_gpos;
};
struct hb_sanitize_with_object_t
diff --git a/src/hb-serialize.hh b/src/hb-serialize.hh
index 61ec025..15eccb6 100644
--- a/src/hb-serialize.hh
+++ b/src/hb-serialize.hh
@@ -113,7 +113,7 @@
{
// Virtual links aren't considered for equality since they don't affect the functionality
// of the object.
- return hb_bytes_t (head, tail - head).hash () ^
+ return hb_bytes_t (head, hb_min (128, tail - head)).hash () ^
real_links.as_bytes ().hash ();
}
@@ -172,8 +172,14 @@
};
snapshot_t snapshot ()
- { return snapshot_t {
- head, tail, current, current->real_links.length, current->virtual_links.length, errors }; }
+ {
+ return snapshot_t {
+ head, tail, current,
+ current ? current->real_links.length : 0,
+ current ? current->virtual_links.length : 0,
+ errors
+ };
+ }
hb_serialize_context_t (void *start_, unsigned int size) :
start ((char *) start_),
@@ -260,7 +266,8 @@
propagate_error (std::forward<Ts> (os)...); }
/* To be called around main operation. */
- template <typename Type>
+ template <typename Type=char>
+ __attribute__((returns_nonnull))
Type *start_serialize ()
{
DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, +1,
@@ -303,6 +310,7 @@
}
template <typename Type = void>
+ __attribute__((returns_nonnull))
Type *push ()
{
if (unlikely (in_error ())) return start_embed<Type> ();
@@ -323,6 +331,8 @@
{
object_t *obj = current;
if (unlikely (!obj)) return;
+ // Allow cleanup when we've error'd out on int overflows which don't compromise
+ // the serializer state.
if (unlikely (in_error() && !only_overflow ())) return;
current = current->next;
@@ -340,7 +350,9 @@
{
object_t *obj = current;
if (unlikely (!obj)) return 0;
- if (unlikely (in_error())) return 0;
+ // Allow cleanup when we've error'd out on int overflows which don't compromise
+ // the serializer state.
+ if (unlikely (in_error() && !only_overflow ())) return 0;
current = current->next;
obj->tail = head;
@@ -405,8 +417,11 @@
// Overflows that happened after the snapshot will be erased by the revert.
if (unlikely (in_error () && !only_overflow ())) return;
assert (snap.current == current);
- current->real_links.shrink (snap.num_real_links);
- current->virtual_links.shrink (snap.num_virtual_links);
+ if (current)
+ {
+ current->real_links.shrink (snap.num_real_links);
+ current->virtual_links.shrink (snap.num_virtual_links);
+ }
errors = snap.errors;
revert (snap.head, snap.tail);
}
@@ -563,13 +578,15 @@
{
unsigned int l = length () % alignment;
if (l)
- allocate_size<void> (alignment - l);
+ (void) allocate_size<void> (alignment - l);
}
template <typename Type = void>
+ __attribute__((returns_nonnull))
Type *start_embed (const Type *obj HB_UNUSED = nullptr) const
{ return reinterpret_cast<Type *> (this->head); }
template <typename Type>
+ __attribute__((returns_nonnull))
Type *start_embed (const Type &obj) const
{ return start_embed (std::addressof (obj)); }
@@ -597,6 +614,7 @@
}
template <typename Type>
+ HB_NODISCARD
Type *allocate_size (size_t size, bool clear = true)
{
if (unlikely (in_error ())) return nullptr;
@@ -618,6 +636,7 @@
{ return this->allocate_size<Type> (Type::min_size); }
template <typename Type>
+ HB_NODISCARD
Type *embed (const Type *obj)
{
unsigned int size = obj->get_size ();
@@ -627,6 +646,7 @@
return ret;
}
template <typename Type>
+ HB_NODISCARD
Type *embed (const Type &obj)
{ return embed (std::addressof (obj)); }
char *embed (const char *obj, unsigned size)
diff --git a/src/hb-set-digest.hh b/src/hb-set-digest.hh
index dab7137..5681641 100644
--- a/src/hb-set-digest.hh
+++ b/src/hb-set-digest.hh
@@ -88,14 +88,19 @@
bool add_range (hb_codepoint_t a, hb_codepoint_t b)
{
+ if (mask == (mask_t) -1) return false;
if ((b >> shift) - (a >> shift) >= mask_bits - 1)
+ {
mask = (mask_t) -1;
- else {
+ return false;
+ }
+ else
+ {
mask_t ma = mask_for (a);
mask_t mb = mask_for (b);
mask |= mb + (mb - ma) - (mb < ma);
+ return true;
}
- return true;
}
template <typename T>
@@ -154,8 +159,7 @@
bool add_range (hb_codepoint_t a, hb_codepoint_t b)
{
- return head.add_range (a, b) &&
- tail.add_range (a, b);
+ return (int) head.add_range (a, b) | (int) tail.add_range (a, b);
}
template <typename T>
void add_array (const T *array, unsigned int count, unsigned int stride=sizeof(T))
diff --git a/src/hb-set.h b/src/hb-set.h
index de53231..192abf6 100644
--- a/src/hb-set.h
+++ b/src/hb-set.h
@@ -43,7 +43,7 @@
*
* Since: 0.9.21
*/
-#define HB_SET_VALUE_INVALID ((hb_codepoint_t) -1)
+#define HB_SET_VALUE_INVALID HB_CODEPOINT_INVALID
/**
* hb_set_t:
diff --git a/src/hb-set.hh b/src/hb-set.hh
index 6048023..7d1c941 100644
--- a/src/hb-set.hh
+++ b/src/hb-set.hh
@@ -115,7 +115,7 @@
/* Sink interface. */
hb_sparseset_t& operator << (hb_codepoint_t v)
{ add (v); return *this; }
- hb_sparseset_t& operator << (const hb_pair_t<hb_codepoint_t, hb_codepoint_t>& range)
+ hb_sparseset_t& operator << (const hb_codepoint_pair_t& range)
{ add_range (range.first, range.second); return *this; }
bool intersects (hb_codepoint_t first, hb_codepoint_t last) const
@@ -174,7 +174,7 @@
hb_set_t& operator << (hb_codepoint_t v)
{ sparseset::operator<< (v); return *this; }
- hb_set_t& operator << (const hb_pair_t<hb_codepoint_t, hb_codepoint_t>& range)
+ hb_set_t& operator << (const hb_codepoint_pair_t& range)
{ sparseset::operator<< (range); return *this; }
};
diff --git a/src/hb-shape.cc b/src/hb-shape.cc
index d9598fc..844f7b9 100644
--- a/src/hb-shape.cc
+++ b/src/hb-shape.cc
@@ -220,7 +220,7 @@
assert (buffer->ensure (text.length));
buffer->have_positions = false;
buffer->len = text.length;
- memcpy (buffer->info, text.arrayZ, text.length * sizeof (buffer->info[0]));
+ hb_memcpy (buffer->info, text.arrayZ, text.length * sizeof (buffer->info[0]));
hb_buffer_set_content_type (buffer, HB_BUFFER_CONTENT_TYPE_UNICODE);
}
diff --git a/src/hb-shaper-list.hh b/src/hb-shaper-list.hh
index 4158b70..f079caf 100644
--- a/src/hb-shaper-list.hh
+++ b/src/hb-shaper-list.hh
@@ -33,6 +33,11 @@
/* v--- Add new shapers in the right place here. */
+#ifdef HAVE_WASM
+/* Only picks up fonts that have a "Wasm" table. */
+HB_SHAPER_IMPLEMENT (wasm)
+#endif
+
#ifdef HAVE_GRAPHITE2
/* Only picks up fonts that have a "Silf" table. */
HB_SHAPER_IMPLEMENT (graphite2)
diff --git a/src/hb-static.cc b/src/hb-static.cc
index a1a2522..c9bd0a6 100644
--- a/src/hb-static.cc
+++ b/src/hb-static.cc
@@ -31,6 +31,7 @@
#include "hb-aat-layout-common.hh"
#include "hb-aat-layout-feat-table.hh"
+#include "hb-cff-interp-common.hh"
#include "hb-ot-layout-common.hh"
#include "hb-ot-cmap-table.hh"
#include "OT/Color/COLR/COLR.hh"
@@ -58,6 +59,8 @@
/* hb_map_t */
const hb_codepoint_t minus_1 = -1;
+static const unsigned char static_endchar_str[] = {OpCode_endchar};
+const unsigned char *endchar_str = static_endchar_str;
/* hb_face_t */
diff --git a/src/hb-subset-accelerator.hh b/src/hb-subset-accelerator.hh
index bb7c62d..9258383 100644
--- a/src/hb-subset-accelerator.hh
+++ b/src/hb-subset-accelerator.hh
@@ -42,7 +42,9 @@
namespace OT {
struct SubtableUnicodesCache;
-};
+struct cff1_subset_accelerator_t;
+struct cff2_subset_accelerator_t;
+}
struct hb_subset_accelerator_t
{
@@ -51,8 +53,8 @@
return &_hb_subset_accelerator_user_data_key;
}
- static hb_subset_accelerator_t* create(const hb_map_t& unicode_to_gid_,
- const hb_multimap_t gid_to_unicodes_,
+ static hb_subset_accelerator_t* create(hb_face_t *source,
+ const hb_map_t& unicode_to_gid_,
const hb_set_t& unicodes_,
bool has_seac_) {
hb_subset_accelerator_t* accel =
@@ -60,8 +62,8 @@
if (unlikely (!accel)) return accel;
- new (accel) hb_subset_accelerator_t (unicode_to_gid_,
- gid_to_unicodes_,
+ new (accel) hb_subset_accelerator_t (source,
+ unicode_to_gid_,
unicodes_,
has_seac_);
@@ -79,36 +81,36 @@
hb_free (accel);
}
- hb_subset_accelerator_t (const hb_map_t& unicode_to_gid_,
- const hb_multimap_t& gid_to_unicodes_,
+ hb_subset_accelerator_t (hb_face_t *source,
+ const hb_map_t& unicode_to_gid_,
const hb_set_t& unicodes_,
bool has_seac_) :
unicode_to_gid(unicode_to_gid_),
- gid_to_unicodes (gid_to_unicodes_),
unicodes(unicodes_),
cmap_cache(nullptr),
destroy_cmap_cache(nullptr),
has_seac(has_seac_),
- cff_accelerator(nullptr),
- destroy_cff_accelerator(nullptr) {}
-
- ~hb_subset_accelerator_t ()
+ source(hb_face_reference (source))
{
- if (cff_accelerator && destroy_cff_accelerator)
- destroy_cff_accelerator ((void*) cff_accelerator);
-
- if (cmap_cache && destroy_cmap_cache)
- destroy_cmap_cache ((void*) cmap_cache);
+ gid_to_unicodes.alloc (unicode_to_gid.get_population ());
+ for (const auto &_ : unicode_to_gid)
+ {
+ auto unicode = _.first;
+ auto gid = _.second;
+ gid_to_unicodes.add (gid, unicode);
+ }
}
+ HB_INTERNAL ~hb_subset_accelerator_t ();
+
// Generic
mutable hb_mutex_t sanitized_table_cache_lock;
mutable hb_hashmap_t<hb_tag_t, hb::unique_ptr<hb_blob_t>> sanitized_table_cache;
- const hb_map_t unicode_to_gid;
- const hb_multimap_t gid_to_unicodes;
- const hb_set_t unicodes;
+ hb_map_t unicode_to_gid;
+ hb_multimap_t gid_to_unicodes;
+ hb_set_t unicodes;
// cmap
const OT::SubtableUnicodesCache* cmap_cache;
@@ -116,8 +118,6 @@
// CFF
bool has_seac;
- const CFF::cff_subset_accelerator_t* cff_accelerator;
- hb_destroy_func_t destroy_cff_accelerator;
// TODO(garretrieger): cumulative glyf checksum map
@@ -128,6 +128,13 @@
unicodes.in_error () ||
sanitized_table_cache.in_error ();
}
+
+ hb_face_t *source;
+#ifndef HB_NO_SUBSET_CFF
+ // These have to be immediately after source:
+ mutable hb_face_lazy_loader_t<OT::cff1_subset_accelerator_t, 1> cff1_accel;
+ mutable hb_face_lazy_loader_t<OT::cff2_subset_accelerator_t, 2> cff2_accel;
+#endif
};
diff --git a/src/hb-subset-cff-common.cc b/src/hb-subset-cff-common.cc
index 6e1b6f7..5e4ea5f 100644
--- a/src/hb-subset-cff-common.cc
+++ b/src/hb-subset-cff-common.cc
@@ -68,24 +68,35 @@
/* use hb_set to determine the subset of font dicts */
hb_set_t set;
hb_codepoint_t prev_fd = CFF_UNDEF_CODE;
- for (hb_codepoint_t i = 0; i < subset_num_glyphs; i++)
+ hb_pair_t<unsigned, hb_codepoint_t> last_range {0, 0};
+ auto it = hb_iter (plan->new_to_old_gid_list);
+ auto _ = *it;
+ for (hb_codepoint_t gid = 0; gid < subset_num_glyphs; gid++)
{
- hb_codepoint_t glyph;
- hb_codepoint_t fd;
- if (!plan->old_gid_for_new_gid (i, &glyph))
+ hb_codepoint_t old_glyph;
+ if (gid == _.first)
+ {
+ old_glyph = _.second;
+ _ = *++it;
+ }
+ else
{
/* fonttools retains FDSelect & font dicts for missing glyphs. do the same */
- glyph = i;
+ old_glyph = gid;
}
- fd = src.get_fd (glyph);
- set.add (fd);
+ if (old_glyph >= last_range.second)
+ last_range = src.get_fd_range (old_glyph);
+ unsigned fd = last_range.first;
if (fd != prev_fd)
{
+ set.add (fd);
num_ranges++;
prev_fd = fd;
- code_pair_t pair = { fd, i };
- fdselect_ranges.push (pair);
+ fdselect_ranges.push (code_pair_t { fd, gid });
+
+ if (gid == old_glyph)
+ gid = hb_min (_.first - 1, last_range.second - 1);
}
}
diff --git a/src/hb-subset-cff-common.hh b/src/hb-subset-cff-common.hh
index ff50b0e..462e99c 100644
--- a/src/hb-subset-cff-common.hh
+++ b/src/hb-subset-cff-common.hh
@@ -480,6 +480,7 @@
const hb_vector_t<parsed_cs_str_vec_t>& parsed_local_subrs) {
cff_subset_accelerator_t* accel =
(cff_subset_accelerator_t*) hb_malloc (sizeof(cff_subset_accelerator_t));
+ if (unlikely (!accel)) return nullptr;
new (accel) cff_subset_accelerator_t (original_blob,
parsed_charstrings,
parsed_global_subrs,
@@ -510,15 +511,21 @@
original_blob = hb_blob_reference (original_blob_);
}
- ~cff_subset_accelerator_t() {
+ ~cff_subset_accelerator_t()
+ {
hb_blob_destroy (original_blob);
- hb_map_destroy (glyph_to_sid_map.get_relaxed ());
+ auto *mapping = glyph_to_sid_map.get_relaxed ();
+ if (mapping)
+ {
+ mapping->~glyph_to_sid_map_t ();
+ hb_free (mapping);
+ }
}
parsed_cs_str_vec_t parsed_charstrings;
parsed_cs_str_vec_t parsed_global_subrs;
hb_vector_t<parsed_cs_str_vec_t> parsed_local_subrs;
- mutable hb_atomic_ptr_t<hb_map_t> glyph_to_sid_map = nullptr;
+ mutable hb_atomic_ptr_t<glyph_to_sid_map_t> glyph_to_sid_map;
private:
hb_blob_t* original_blob;
@@ -600,9 +607,8 @@
* no optimization based on usage counts. fonttools doesn't appear doing that either.
*/
- resize (closure->get_population ());
- hb_codepoint_t old_num = HB_SET_VALUE_INVALID;
- while (hb_set_next (closure, &old_num))
+ alloc (closure->get_population ());
+ for (auto old_num : *closure)
add (old_num);
if (get_population () < 1240)
@@ -672,8 +678,8 @@
{
unsigned fd_count = acc.fdCount;
const cff_subset_accelerator_t* cff_accelerator = nullptr;
- if (plan->accelerator && plan->accelerator->cff_accelerator) {
- cff_accelerator = plan->accelerator->cff_accelerator;
+ if (acc.cff_accelerator) {
+ cff_accelerator = acc.cff_accelerator;
fd_count = cff_accelerator->parsed_local_subrs.length;
}
@@ -709,14 +715,13 @@
}
/* phase 1 & 2 */
- for (unsigned int i = 0; i < plan->num_output_glyphs (); i++)
+ for (auto _ : plan->new_to_old_gid_list)
{
- hb_codepoint_t glyph;
- if (!plan->old_gid_for_new_gid (i, &glyph))
- continue;
+ hb_codepoint_t new_glyph = _.first;
+ hb_codepoint_t old_glyph = _.second;
- const hb_ubytes_t str = (*acc.charStrings)[glyph];
- unsigned int fd = acc.fdSelect->get_fd (glyph);
+ const hb_ubytes_t str = (*acc.charStrings)[old_glyph];
+ unsigned int fd = acc.fdSelect->get_fd (old_glyph);
if (unlikely (fd >= acc.fdCount))
return false;
@@ -725,9 +730,9 @@
// parsed string already exists in accelerator, copy it and move
// on.
if (cached_charstrings)
- cached_charstrings[i] = &cff_accelerator->parsed_charstrings[glyph];
+ cached_charstrings[new_glyph] = &cff_accelerator->parsed_charstrings[old_glyph];
else
- parsed_charstrings[i] = cff_accelerator->parsed_charstrings[glyph];
+ parsed_charstrings[new_glyph] = cff_accelerator->parsed_charstrings[old_glyph];
continue;
}
@@ -735,8 +740,8 @@
ENV env (str, acc, fd);
cs_interpreter_t<ENV, OPSET, subr_subset_param_t> interp (env);
- parsed_charstrings[i].alloc (str.length);
- subr_subset_param_t param (&parsed_charstrings[i],
+ parsed_charstrings[new_glyph].alloc (str.length);
+ subr_subset_param_t param (&parsed_charstrings[new_glyph],
&parsed_global_subrs_storage,
&parsed_local_subrs_storage[fd],
&closures.global_closure,
@@ -747,12 +752,12 @@
return false;
/* complete parsed string esp. copy CFF1 width or CFF2 vsindex to the parsed charstring for encoding */
- SUBSETTER::complete_parsed_str (interp.env, param, parsed_charstrings[i]);
+ SUBSETTER::complete_parsed_str (interp.env, param, parsed_charstrings[new_glyph]);
/* mark hint ops and arguments for drop */
if ((plan->flags & HB_SUBSET_FLAGS_NO_HINTING) || plan->inprogress_accelerator)
{
- subr_subset_param_t param (&parsed_charstrings[i],
+ subr_subset_param_t param (&parsed_charstrings[new_glyph],
&parsed_global_subrs_storage,
&parsed_local_subrs_storage[fd],
&closures.global_closure,
@@ -760,21 +765,21 @@
plan->flags & HB_SUBSET_FLAGS_NO_HINTING);
drop_hints_param_t drop;
- if (drop_hints_in_str (parsed_charstrings[i], param, drop))
+ if (drop_hints_in_str (parsed_charstrings[new_glyph], param, drop))
{
- parsed_charstrings[i].set_hint_dropped ();
+ parsed_charstrings[new_glyph].set_hint_dropped ();
if (drop.vsindex_dropped)
- parsed_charstrings[i].set_vsindex_dropped ();
+ parsed_charstrings[new_glyph].set_vsindex_dropped ();
}
}
- /* Doing this here one by one instead of compacting all at the en
+ /* Doing this here one by one instead of compacting all at the end
* has massive peak-memory saving.
*
* The compacting both saves memory and makes further operations
* faster.
*/
- parsed_charstrings[i].compact ();
+ parsed_charstrings[new_glyph].compact ();
}
/* Since parsed strings were loaded from accelerator, we still need
@@ -797,23 +802,40 @@
bool encode_charstrings (str_buff_vec_t &buffArray, bool encode_prefix = true) const
{
- if (unlikely (!buffArray.resize_exact (plan->num_output_glyphs ())))
+ unsigned num_glyphs = plan->num_output_glyphs ();
+ if (unlikely (!buffArray.resize_exact (num_glyphs)))
return false;
- for (unsigned int i = 0; i < plan->num_output_glyphs (); i++)
+ hb_codepoint_t last = 0;
+ for (auto _ : plan->new_to_old_gid_list)
{
- hb_codepoint_t glyph;
- if (!plan->old_gid_for_new_gid (i, &glyph))
- {
- /* add an endchar only charstring for a missing glyph if CFF1 */
- if (endchar_op != OpCode_Invalid) buffArray.arrayZ[i].push (endchar_op);
- continue;
- }
- unsigned int fd = acc.fdSelect->get_fd (glyph);
+ hb_codepoint_t gid = _.first;
+ hb_codepoint_t old_glyph = _.second;
+
+ if (endchar_op != OpCode_Invalid)
+ for (; last < gid; last++)
+ {
+ // Hack to point vector to static string.
+ auto &b = buffArray.arrayZ[last];
+ b.length = 1;
+ b.arrayZ = const_cast<unsigned char *>(endchar_str);
+ }
+
+ last++; // Skip over gid
+ unsigned int fd = acc.fdSelect->get_fd (old_glyph);
if (unlikely (fd >= acc.fdCount))
return false;
- if (unlikely (!encode_str (get_parsed_charstring (i), fd, buffArray.arrayZ[i], encode_prefix)))
+ if (unlikely (!encode_str (get_parsed_charstring (gid), fd, buffArray.arrayZ[gid], encode_prefix)))
return false;
}
+ if (endchar_op != OpCode_Invalid)
+ for (; last < num_glyphs; last++)
+ {
+ // Hack to point vector to static string.
+ auto &b = buffArray.arrayZ[last];
+ b.length = 1;
+ b.arrayZ = const_cast<unsigned char *>(endchar_str);
+ }
+
return true;
}
@@ -980,24 +1002,23 @@
const hb_vector_t<parsed_cs_str_vec_t>& local_subrs)
{
closures.reset ();
- for (unsigned int i = 0; i < plan->num_output_glyphs (); i++)
+ for (auto _ : plan->new_to_old_gid_list)
{
- hb_codepoint_t glyph;
- if (!plan->old_gid_for_new_gid (i, &glyph))
- continue;
- unsigned int fd = acc.fdSelect->get_fd (glyph);
+ hb_codepoint_t new_glyph = _.first;
+ hb_codepoint_t old_glyph = _.second;
+ unsigned int fd = acc.fdSelect->get_fd (old_glyph);
if (unlikely (fd >= acc.fdCount))
return false;
// Note: const cast is safe here because the collect_subr_refs_in_str only performs a
// closure and does not modify any of the charstrings.
- subr_subset_param_t param (const_cast<parsed_cs_str_t*> (&get_parsed_charstring (i)),
+ subr_subset_param_t param (const_cast<parsed_cs_str_t*> (&get_parsed_charstring (new_glyph)),
const_cast<parsed_cs_str_vec_t*> (&global_subrs),
const_cast<parsed_cs_str_vec_t*> (&local_subrs[fd]),
&closures.global_closure,
&closures.local_closures[fd],
plan->flags & HB_SUBSET_FLAGS_NO_HINTING);
- collect_subr_refs_in_str (get_parsed_charstring (i), param);
+ collect_subr_refs_in_str (get_parsed_charstring (new_glyph), param);
}
return true;
@@ -1105,14 +1126,11 @@
compact_parsed_subrs ();
- plan->inprogress_accelerator->cff_accelerator =
+ acc.cff_accelerator =
cff_subset_accelerator_t::create(acc.blob,
parsed_charstrings,
parsed_global_subrs_storage,
parsed_local_subrs_storage);
- plan->inprogress_accelerator->destroy_cff_accelerator =
- cff_subset_accelerator_t::destroy;
-
}
const parsed_cs_str_t& get_parsed_charstring (unsigned i) const
diff --git a/src/hb-subset-cff1.cc b/src/hb-subset-cff1.cc
index 1d7ed64..872cba6 100644
--- a/src/hb-subset-cff1.cc
+++ b/src/hb-subset-cff1.cc
@@ -32,36 +32,59 @@
#include "hb-ot-cff1-table.hh"
#include "hb-set.h"
#include "hb-bimap.hh"
-#include "hb-subset-cff1.hh"
#include "hb-subset-plan.hh"
#include "hb-subset-cff-common.hh"
#include "hb-cff1-interp-cs.hh"
using namespace CFF;
-struct remap_sid_t : hb_inc_bimap_t
+struct remap_sid_t
{
+ unsigned get_population () const { return vector.length; }
+
+ void alloc (unsigned size)
+ {
+ map.alloc (size);
+ vector.alloc (size, true);
+ }
+
+ bool in_error () const
+ { return map.in_error () || vector.in_error (); }
+
unsigned int add (unsigned int sid)
{
- if ((sid != CFF_UNDEF_SID) && !is_std_std (sid))
- return offset_sid (hb_inc_bimap_t::add (unoffset_sid (sid)));
- else
+ if (is_std_str (sid) || (sid == CFF_UNDEF_SID))
return sid;
+
+ sid = unoffset_sid (sid);
+ unsigned v = next;
+ if (map.set (sid, v, false))
+ {
+ vector.push (sid);
+ next++;
+ }
+ else
+ v = map.get (sid); // already exists
+ return offset_sid (v);
}
unsigned int operator[] (unsigned int sid) const
{
- if (is_std_std (sid) || (sid == CFF_UNDEF_SID))
+ if (is_std_str (sid) || (sid == CFF_UNDEF_SID))
return sid;
- else
- return offset_sid (get (unoffset_sid (sid)));
+
+ return offset_sid (map.get (unoffset_sid (sid)));
}
static const unsigned int num_std_strings = 391;
- static bool is_std_std (unsigned int sid) { return sid < num_std_strings; }
+ static bool is_std_str (unsigned int sid) { return sid < num_std_strings; }
static unsigned int offset_sid (unsigned int sid) { return sid + num_std_strings; }
static unsigned int unoffset_sid (unsigned int sid) { return sid - num_std_strings; }
+ unsigned next = 0;
+
+ hb_map_t map;
+ hb_vector_t<unsigned> vector;
};
struct cff1_sub_table_info_t : cff_sub_table_info_t
@@ -271,16 +294,17 @@
/* replace the first glyph ID in the "glyph" field each range with a nLeft value */
bool complete (unsigned int last_glyph)
{
- bool two_byte = false;
+ hb_codepoint_t all_glyphs = 0;
unsigned count = this->length;
for (unsigned int i = count; i; i--)
{
code_pair_t &pair = arrayZ[i - 1];
unsigned int nLeft = last_glyph - pair.glyph - 1;
- two_byte |= nLeft >= 0x100;
+ all_glyphs |= nLeft;
last_glyph = pair.glyph;
pair.glyph = nLeft;
}
+ bool two_byte = all_glyphs >= 0x100;
return two_byte;
}
};
@@ -391,8 +415,10 @@
}
};
-struct cff_subset_plan {
- cff_subset_plan ()
+namespace OT {
+struct cff1_subset_plan
+{
+ cff1_subset_plan ()
{
for (unsigned int i = 0; i < name_dict_values_t::ValCount; i++)
topDictModSIDs[i] = CFF_UNDEF_SID;
@@ -402,7 +428,7 @@
{
const Encoding *encoding = acc.encoding;
unsigned int size0, size1;
- hb_codepoint_t code, last_code = CFF_UNDEF_CODE;
+ unsigned code, last_code = CFF_UNDEF_CODE - 1;
hb_vector_t<hb_codepoint_t> supp_codes;
if (unlikely (!subset_enc_code_ranges.resize (0)))
@@ -413,39 +439,42 @@
supp_codes.init ();
+ code_pair_t glyph_to_sid_cache {0, HB_CODEPOINT_INVALID};
subset_enc_num_codes = plan->num_output_glyphs () - 1;
unsigned int glyph;
- for (glyph = 1; glyph < plan->num_output_glyphs (); glyph++)
+ auto it = hb_iter (plan->new_to_old_gid_list);
+ if (it->first == 0) it++;
+ auto _ = *it;
+ for (glyph = 1; glyph < num_glyphs; glyph++)
{
- hb_codepoint_t old_glyph;
- if (!plan->old_gid_for_new_gid (glyph, &old_glyph))
+ hb_codepoint_t old_glyph;
+ if (glyph == _.first)
{
- /* Retain the code for the old missing glyph ID */
+ old_glyph = _.second;
+ _ = *++it;
+ }
+ else
+ {
+ /* Retain the SID for the old missing glyph ID */
old_glyph = glyph;
}
- code = acc.glyph_to_code (old_glyph);
+ code = acc.glyph_to_code (old_glyph, &glyph_to_sid_cache);
if (code == CFF_UNDEF_CODE)
{
subset_enc_num_codes = glyph - 1;
break;
}
- if ((last_code == CFF_UNDEF_CODE) || (code != last_code + 1))
- {
- code_pair_t pair = { code, glyph };
- subset_enc_code_ranges.push (pair);
- }
+ if (code != last_code + 1)
+ subset_enc_code_ranges.push (code_pair_t {code, glyph});
last_code = code;
if (encoding != &Null (Encoding))
{
- hb_codepoint_t sid = acc.glyph_to_sid (old_glyph);
+ hb_codepoint_t sid = acc.glyph_to_sid (old_glyph, &glyph_to_sid_cache);
encoding->get_supplement_codes (sid, supp_codes);
for (unsigned int i = 0; i < supp_codes.length; i++)
- {
- code_pair_t pair = { supp_codes[i], sid };
- subset_enc_supp_codes.push (pair);
- }
+ subset_enc_supp_codes.push (code_pair_t {supp_codes[i], sid});
}
}
supp_codes.fini ();
@@ -462,65 +491,93 @@
subset_enc_format = 1;
}
- void plan_subset_charset (const OT::cff1::accelerator_subset_t &acc, hb_subset_plan_t *plan)
+ bool plan_subset_charset (const OT::cff1::accelerator_subset_t &acc, hb_subset_plan_t *plan)
{
unsigned int size0, size_ranges;
- hb_codepoint_t sid, last_sid = CFF_UNDEF_CODE;
+ unsigned last_sid = CFF_UNDEF_CODE - 1;
if (unlikely (!subset_charset_ranges.resize (0)))
{
plan->check_success (false);
- return;
+ return false;
}
- hb_map_t *glyph_to_sid_map = (plan->accelerator && plan->accelerator->cff_accelerator) ?
- plan->accelerator->cff_accelerator->glyph_to_sid_map :
- nullptr;
+ code_pair_t glyph_to_sid_cache {0, HB_CODEPOINT_INVALID};
+
+ unsigned num_glyphs = plan->num_output_glyphs ();
+
+ if (unlikely (!subset_charset_ranges.alloc (hb_min (num_glyphs,
+ acc.num_charset_entries))))
+ {
+ plan->check_success (false);
+ return false;
+ }
+
+ glyph_to_sid_map_t *glyph_to_sid_map = acc.cff_accelerator ?
+ acc.cff_accelerator->glyph_to_sid_map.get_acquire () :
+ nullptr;
bool created_map = false;
- if (!glyph_to_sid_map &&
- ((plan->accelerator && plan->accelerator->cff_accelerator) ||
- plan->num_output_glyphs () > plan->source->get_num_glyphs () / 8.))
+ if (!glyph_to_sid_map && acc.cff_accelerator)
{
created_map = true;
glyph_to_sid_map = acc.create_glyph_to_sid_map ();
}
- unsigned int glyph;
- for (glyph = 1; glyph < plan->num_output_glyphs (); glyph++)
+ auto it = hb_iter (plan->new_to_old_gid_list);
+ if (it->first == 0) it++;
+ auto _ = *it;
+ bool not_is_cid = !acc.is_CID ();
+ bool skip = !not_is_cid && glyph_to_sid_map;
+ if (not_is_cid)
+ sidmap.alloc (num_glyphs);
+ for (hb_codepoint_t glyph = 1; glyph < num_glyphs; glyph++)
{
- hb_codepoint_t old_glyph;
- if (!plan->old_gid_for_new_gid (glyph, &old_glyph))
+ hb_codepoint_t old_glyph;
+ if (glyph == _.first)
+ {
+ old_glyph = _.second;
+ _ = *++it;
+ }
+ else
{
/* Retain the SID for the old missing glyph ID */
old_glyph = glyph;
}
- sid = glyph_to_sid_map ? glyph_to_sid_map->get (old_glyph) : acc.glyph_to_sid (old_glyph);
+ unsigned sid = glyph_to_sid_map ?
+ glyph_to_sid_map->arrayZ[old_glyph].code :
+ acc.glyph_to_sid (old_glyph, &glyph_to_sid_cache);
- if (!acc.is_CID ())
+ if (not_is_cid)
sid = sidmap.add (sid);
- if ((last_sid == CFF_UNDEF_CODE) || (sid != last_sid + 1))
+ if (sid != last_sid + 1)
+ subset_charset_ranges.push (code_pair_t {sid, glyph});
+
+ if (glyph == old_glyph && skip)
{
- code_pair_t pair = { sid, glyph };
- subset_charset_ranges.push (pair);
+ glyph = hb_min (_.first - 1, glyph_to_sid_map->arrayZ[old_glyph].glyph);
+ sid += glyph - old_glyph;
}
last_sid = sid;
}
if (created_map)
{
- if (!(plan->accelerator && plan->accelerator->cff_accelerator) ||
- !plan->accelerator->cff_accelerator->glyph_to_sid_map.cmpexch (nullptr, glyph_to_sid_map))
- hb_map_destroy (glyph_to_sid_map);
+ if ((!plan->accelerator && acc.cff_accelerator) ||
+ !acc.cff_accelerator->glyph_to_sid_map.cmpexch (nullptr, glyph_to_sid_map))
+ {
+ glyph_to_sid_map->~glyph_to_sid_map_t ();
+ hb_free (glyph_to_sid_map);
+ }
}
- bool two_byte = subset_charset_ranges.complete (glyph);
+ bool two_byte = subset_charset_ranges.complete (num_glyphs);
- size0 = Charset0::min_size + HBUINT16::static_size * (plan->num_output_glyphs () - 1);
+ size0 = Charset0::get_size (plan->num_output_glyphs ());
if (!two_byte)
- size_ranges = Charset1::min_size + Charset1_Range::static_size * subset_charset_ranges.length;
+ size_ranges = Charset1::get_size_for_ranges (subset_charset_ranges.length);
else
- size_ranges = Charset2::min_size + Charset2_Range::static_size * subset_charset_ranges.length;
+ size_ranges = Charset2::get_size_for_ranges (subset_charset_ranges.length);
if (size0 < size_ranges)
subset_charset_format = 0;
@@ -528,19 +585,18 @@
subset_charset_format = 1;
else
subset_charset_format = 2;
+
+ return true;
}
bool collect_sids_in_dicts (const OT::cff1::accelerator_subset_t &acc)
{
- sidmap.reset ();
-
for (unsigned int i = 0; i < name_dict_values_t::ValCount; i++)
{
unsigned int sid = acc.topDict.nameSIDs[i];
if (sid != CFF_UNDEF_SID)
{
- (void)sidmap.add (sid);
- topDictModSIDs[i] = sidmap[sid];
+ topDictModSIDs[i] = sidmap.add (sid);
}
}
@@ -564,19 +620,18 @@
drop_hints = plan->flags & HB_SUBSET_FLAGS_NO_HINTING;
desubroutinize = plan->flags & HB_SUBSET_FLAGS_DESUBROUTINIZE;
- /* check whether the subset renumbers any glyph IDs */
- gid_renum = false;
- for (hb_codepoint_t new_glyph = 0; new_glyph < plan->num_output_glyphs (); new_glyph++)
- {
- if (!plan->old_gid_for_new_gid(new_glyph, &old_glyph))
- continue;
- if (new_glyph != old_glyph) {
- gid_renum = true;
- break;
+ subset_charset = !acc.is_predef_charset ();
+ if (!subset_charset)
+ /* check whether the subset renumbers any glyph IDs */
+ for (const auto &_ : plan->new_to_old_gid_list)
+ {
+ if (_.first != _.second)
+ {
+ subset_charset = true;
+ break;
+ }
}
- }
- subset_charset = gid_renum || !acc.is_predef_charset ();
subset_encoding = !acc.is_CID() && !acc.is_predef_encoding ();
/* top dict INDEX */
@@ -618,7 +673,8 @@
if (unlikely (sidmap.get_population () > 0x8000)) /* assumption: a dict won't reference that many strings */
return false;
- if (subset_charset) plan_subset_charset (acc, plan);
+ if (subset_charset && !plan_subset_charset (acc, plan))
+ return false;
topdict_mod.reassignSIDs (sidmap);
}
@@ -682,8 +738,9 @@
;
}
- return ((subset_charstrings.length == plan->num_output_glyphs ())
- && (fontdicts_mod.length == subset_fdcount));
+ return !plan->in_error () &&
+ (subset_charstrings.length == plan->num_output_glyphs ()) &&
+ (fontdicts_mod.length == subset_fdcount);
}
cff1_top_dict_values_mod_t topdict_mod;
@@ -722,24 +779,22 @@
bool desubroutinize = false;
};
+} // namespace OT
-static bool _serialize_cff1 (hb_serialize_context_t *c,
- cff_subset_plan &plan,
- const OT::cff1::accelerator_subset_t &acc,
- unsigned int num_glyphs)
+bool
+OT::cff1::accelerator_subset_t::serialize (hb_serialize_context_t *c,
+ struct OT::cff1_subset_plan &plan) const
{
/* private dicts & local subrs */
- for (int i = (int)acc.privateDicts.length; --i >= 0 ;)
+ for (int i = (int) privateDicts.length; --i >= 0 ;)
{
if (plan.fdmap.has (i))
{
objidx_t subrs_link = 0;
if (plan.subset_localsubrs[i].length > 0)
{
- CFF1Subrs *dest = c->start_embed <CFF1Subrs> ();
- if (unlikely (!dest)) return false;
- c->push ();
- if (likely (dest && dest->serialize (c, plan.subset_localsubrs[i])))
+ auto *dest = c->push <CFF1Subrs> ();
+ if (likely (dest->serialize (c, plan.subset_localsubrs[i])))
subrs_link = c->pop_pack ();
else
{
@@ -748,12 +803,10 @@
}
}
- PrivateDict *pd = c->start_embed<PrivateDict> ();
- if (unlikely (!pd)) return false;
- c->push ();
+ auto *pd = c->push<PrivateDict> ();
cff1_private_dict_op_serializer_t privSzr (plan.desubroutinize, plan.drop_hints);
/* N.B. local subrs immediately follows its corresponding private dict. i.e., subr offset == private dict size */
- if (likely (pd->serialize (c, acc.privateDicts[i], privSzr, subrs_link)))
+ if (likely (pd->serialize (c, privateDicts[i], privSzr, subrs_link)))
{
unsigned fd = plan.fdmap[i];
plan.fontdicts_mod[fd].privateDictInfo.size = c->length ();
@@ -767,21 +820,20 @@
}
}
- if (!acc.is_CID ())
+ if (!is_CID ())
plan.info.privateDictInfo = plan.fontdicts_mod[0].privateDictInfo;
/* CharStrings */
{
c->push<CFF1CharStrings> ();
- unsigned total_size = CFF1CharStrings::total_size (plan.subset_charstrings);
+ unsigned data_size = 0;
+ unsigned total_size = CFF1CharStrings::total_size (plan.subset_charstrings, &data_size);
if (unlikely (!c->start_zerocopy (total_size)))
return false;
- CFF1CharStrings *cs = c->start_embed<CFF1CharStrings> ();
- if (unlikely (!cs)) return false;
-
- if (likely (cs->serialize (c, plan.subset_charstrings)))
+ auto *cs = c->start_embed<CFF1CharStrings> ();
+ if (likely (cs->serialize (c, plan.subset_charstrings, &data_size)))
plan.info.char_strings_link = c->pop_pack (false);
else
{
@@ -791,11 +843,9 @@
}
/* FDArray (FD Index) */
- if (acc.fdArray != &Null (CFF1FDArray))
+ if (fdArray != &Null (CFF1FDArray))
{
- CFF1FDArray *fda = c->start_embed<CFF1FDArray> ();
- if (unlikely (!fda)) return false;
- c->push ();
+ auto *fda = c->push<CFF1FDArray> ();
cff1_font_dict_op_serializer_t fontSzr;
auto it = + hb_zip (+ hb_iter (plan.fontdicts_mod), + hb_iter (plan.fontdicts_mod));
if (likely (fda->serialize (c, it, fontSzr)))
@@ -808,10 +858,10 @@
}
/* FDSelect */
- if (acc.fdSelect != &Null (CFF1FDSelect))
+ if (fdSelect != &Null (CFF1FDSelect))
{
c->push ();
- if (likely (hb_serialize_cff_fdselect (c, num_glyphs, *acc.fdSelect, acc.fdCount,
+ if (likely (hb_serialize_cff_fdselect (c, plan.num_glyphs, *fdSelect, fdCount,
plan.subset_fdselect_format, plan.info.fd_select.size,
plan.subset_fdselect_ranges)))
plan.info.fd_select.link = c->pop_pack ();
@@ -825,9 +875,7 @@
/* Charset */
if (plan.subset_charset)
{
- Charset *dest = c->start_embed<Charset> ();
- if (unlikely (!dest)) return false;
- c->push ();
+ auto *dest = c->push<Charset> ();
if (likely (dest->serialize (c,
plan.subset_charset_format,
plan.num_glyphs,
@@ -843,9 +891,7 @@
/* Encoding */
if (plan.subset_encoding)
{
- Encoding *dest = c->start_embed<Encoding> ();
- if (unlikely (!dest)) return false;
- c->push ();
+ auto *dest = c->push<Encoding> ();
if (likely (dest->serialize (c,
plan.subset_enc_format,
plan.subset_enc_num_codes,
@@ -861,9 +907,7 @@
/* global subrs */
{
- c->push ();
- CFF1Subrs *dest = c->start_embed <CFF1Subrs> ();
- if (unlikely (!dest)) return false;
+ auto *dest = c->push <CFF1Subrs> ();
if (likely (dest->serialize (c, plan.subset_globalsubrs)))
c->pop_pack (false);
else
@@ -875,10 +919,9 @@
/* String INDEX */
{
- CFF1StringIndex *dest = c->start_embed<CFF1StringIndex> ();
- if (unlikely (!dest)) return false;
- c->push ();
- if (likely (dest->serialize (c, *acc.stringIndex, plan.sidmap)))
+ auto *dest = c->push<CFF1StringIndex> ();
+ if (likely (!plan.sidmap.in_error () &&
+ dest->serialize (c, *stringIndex, plan.sidmap.vector)))
c->pop_pack ();
else
{
@@ -898,14 +941,12 @@
cff->offSize = 4; /* unused? */
/* name INDEX */
- if (unlikely (!(*acc.nameIndex).copy (c))) return false;
+ if (unlikely (!c->embed (*nameIndex))) return false;
/* top dict INDEX */
{
/* serialize singleton TopDict */
- TopDict *top = c->start_embed<TopDict> ();
- if (!top) return false;
- c->push ();
+ auto *top = c->push<TopDict> ();
cff1_top_dict_op_serializer_t topSzr;
unsigned top_size = 0;
top_dict_modifiers_t modifier (plan.info, plan.topDictModSIDs);
@@ -920,36 +961,23 @@
return false;
}
/* serialize INDEX header for above */
- CFF1Index *dest = c->start_embed<CFF1Index> ();
- if (!dest) return false;
- return dest->serialize_header (c, hb_iter (hb_array_t<unsigned> (&top_size, 1)));
+ auto *dest = c->start_embed<CFF1Index> ();
+ return dest->serialize_header (c, hb_iter (&top_size, 1), top_size);
}
}
-static bool
-_hb_subset_cff1 (const OT::cff1::accelerator_subset_t &acc,
- hb_subset_context_t *c)
+bool
+OT::cff1::accelerator_subset_t::subset (hb_subset_context_t *c) const
{
- cff_subset_plan cff_plan;
+ cff1_subset_plan cff_plan;
- if (unlikely (!cff_plan.create (acc, c->plan)))
+ if (unlikely (!cff_plan.create (*this, c->plan)))
{
DEBUG_MSG(SUBSET, nullptr, "Failed to generate a cff subsetting plan.");
return false;
}
- return _serialize_cff1 (c->serializer, cff_plan, acc, c->plan->num_output_glyphs ());
-}
-
-bool
-hb_subset_cff1 (hb_subset_context_t *c)
-{
- OT::cff1::accelerator_subset_t acc;
- acc.init (c->plan->source);
- bool result = likely (acc.is_valid ()) && _hb_subset_cff1 (acc, c);
- acc.fini ();
-
- return result;
+ return serialize (c->serializer, cff_plan);
}
diff --git a/src/hb-subset-cff2.cc b/src/hb-subset-cff2.cc
index 8ab4620..3c52fb9 100644
--- a/src/hb-subset-cff2.cc
+++ b/src/hb-subset-cff2.cc
@@ -31,7 +31,6 @@
#include "hb-open-type.hh"
#include "hb-ot-cff2-table.hh"
#include "hb-set.h"
-#include "hb-subset-cff2.hh"
#include "hb-subset-plan.hh"
#include "hb-subset-cff-common.hh"
#include "hb-cff2-interp-cs.hh"
@@ -422,11 +421,17 @@
};
+namespace OT {
struct cff2_subset_plan
{
bool create (const OT::cff2::accelerator_subset_t &acc,
hb_subset_plan_t *plan)
{
+ /* make sure notdef is first */
+ hb_codepoint_t old_glyph;
+ if (!plan->old_gid_for_new_gid (0, &old_glyph) || (old_glyph != 0)) return false;
+
+ num_glyphs = plan->num_output_glyphs ();
orig_fdcount = acc.fdArray->count;
drop_hints = plan->flags & HB_SUBSET_FLAGS_NO_HINTING;
@@ -489,6 +494,7 @@
cff2_sub_table_info_t info;
+ unsigned int num_glyphs;
unsigned int orig_fdcount = 0;
unsigned int subset_fdcount = 1;
unsigned int subset_fdselect_size = 0;
@@ -505,18 +511,18 @@
bool drop_hints = false;
bool desubroutinize = false;
};
+} // namespace OT
-static bool _serialize_cff2 (hb_serialize_context_t *c,
- cff2_subset_plan &plan,
- const OT::cff2::accelerator_subset_t &acc,
- unsigned int num_glyphs,
- hb_array_t<int> normalized_coords)
+bool
+OT::cff2::accelerator_subset_t::serialize (hb_serialize_context_t *c,
+ struct cff2_subset_plan &plan,
+ hb_array_t<int> normalized_coords) const
{
/* private dicts & local subrs */
hb_vector_t<table_info_t> private_dict_infos;
if (unlikely (!private_dict_infos.resize (plan.subset_fdcount))) return false;
- for (int i = (int)acc.privateDicts.length; --i >= 0 ;)
+ for (int i = (int)privateDicts.length; --i >= 0 ;)
{
if (plan.fdmap.has (i))
{
@@ -524,9 +530,7 @@
if (plan.subset_localsubrs[i].length > 0)
{
- CFF2Subrs *dest = c->start_embed <CFF2Subrs> ();
- if (unlikely (!dest)) return false;
- c->push ();
+ auto *dest = c->push <CFF2Subrs> ();
if (likely (dest->serialize (c, plan.subset_localsubrs[i])))
subrs_link = c->pop_pack (false);
else
@@ -535,12 +539,10 @@
return false;
}
}
- PrivateDict *pd = c->start_embed<PrivateDict> ();
- if (unlikely (!pd)) return false;
- c->push ();
+ auto *pd = c->push<PrivateDict> ();
cff2_private_dict_op_serializer_t privSzr (plan.desubroutinize, plan.drop_hints, plan.pinned,
- acc.varStore, normalized_coords);
- if (likely (pd->serialize (c, acc.privateDicts[i], privSzr, subrs_link)))
+ varStore, normalized_coords);
+ if (likely (pd->serialize (c, privateDicts[i], privSzr, subrs_link)))
{
unsigned fd = plan.fdmap[i];
private_dict_infos[fd].size = c->length ();
@@ -558,14 +560,13 @@
{
c->push ();
- unsigned total_size = CFF2CharStrings::total_size (plan.subset_charstrings);
+ unsigned data_size = 0;
+ unsigned total_size = CFF2CharStrings::total_size (plan.subset_charstrings, &data_size);
if (unlikely (!c->start_zerocopy (total_size)))
return false;
- CFF2CharStrings *cs = c->start_embed<CFF2CharStrings> ();
- if (unlikely (!cs)) return false;
-
- if (likely (cs->serialize (c, plan.subset_charstrings)))
+ auto *cs = c->start_embed<CFF2CharStrings> ();
+ if (likely (cs->serialize (c, plan.subset_charstrings, &data_size)))
plan.info.char_strings_link = c->pop_pack (false);
else
{
@@ -575,10 +576,10 @@
}
/* FDSelect */
- if (acc.fdSelect != &Null (CFF2FDSelect))
+ if (fdSelect != &Null (CFF2FDSelect))
{
c->push ();
- if (likely (hb_serialize_cff_fdselect (c, num_glyphs, *(const FDSelect *)acc.fdSelect,
+ if (likely (hb_serialize_cff_fdselect (c, plan.num_glyphs, *(const FDSelect *)fdSelect,
plan.orig_fdcount,
plan.subset_fdselect_format, plan.subset_fdselect_size,
plan.subset_fdselect_ranges)))
@@ -592,27 +593,32 @@
/* FDArray (FD Index) */
{
- c->push ();
- CFF2FDArray *fda = c->start_embed<CFF2FDArray> ();
- if (unlikely (!fda)) return false;
+ auto *fda = c->push<CFF2FDArray> ();
cff_font_dict_op_serializer_t fontSzr;
auto it =
- + hb_zip (+ hb_iter (acc.fontDicts)
+ + hb_zip (+ hb_iter (fontDicts)
| hb_filter ([&] (const cff2_font_dict_values_t &_)
- { return plan.fdmap.has (&_ - &acc.fontDicts[0]); }),
+ { return plan.fdmap.has (&_ - &fontDicts[0]); }),
hb_iter (private_dict_infos))
;
- if (unlikely (!fda->serialize (c, it, fontSzr))) return false;
+ if (unlikely (!fda->serialize (c, it, fontSzr)))
+ {
+ c->pop_discard ();
+ return false;
+ }
plan.info.fd_array_link = c->pop_pack (false);
}
/* variation store */
- if (acc.varStore != &Null (CFF2VariationStore) &&
+ if (varStore != &Null (CFF2VariationStore) &&
!plan.pinned)
{
- c->push ();
- CFF2VariationStore *dest = c->start_embed<CFF2VariationStore> ();
- if (unlikely (!dest || !dest->serialize (c, acc.varStore))) return false;
+ auto *dest = c->push<CFF2VariationStore> ();
+ if (unlikely (!dest->serialize (c, varStore)))
+ {
+ c->pop_discard ();
+ return false;
+ }
plan.info.var_store_link = c->pop_pack (false);
}
@@ -628,34 +634,25 @@
{
TopDict &dict = cff2 + cff2->topDict;
cff2_top_dict_op_serializer_t topSzr;
- if (unlikely (!dict.serialize (c, acc.topDict, topSzr, plan.info))) return false;
+ if (unlikely (!dict.serialize (c, topDict, topSzr, plan.info))) return false;
cff2->topDictSize = c->head - (const char *)&dict;
}
/* global subrs */
{
- CFF2Subrs *dest = c->start_embed <CFF2Subrs> ();
- if (unlikely (!dest)) return false;
+ auto *dest = c->start_embed <CFF2Subrs> ();
return dest->serialize (c, plan.subset_globalsubrs);
}
}
-static bool
-_hb_subset_cff2 (const OT::cff2::accelerator_subset_t &acc,
- hb_subset_context_t *c)
+bool
+OT::cff2::accelerator_subset_t::subset (hb_subset_context_t *c) const
{
cff2_subset_plan cff2_plan;
- if (unlikely (!cff2_plan.create (acc, c->plan))) return false;
- return _serialize_cff2 (c->serializer, cff2_plan, acc, c->plan->num_output_glyphs (),
- c->plan->normalized_coords.as_array ());
-}
-
-bool
-hb_subset_cff2 (hb_subset_context_t *c)
-{
- OT::cff2::accelerator_subset_t acc (c->plan->source);
- return acc.is_valid () && _hb_subset_cff2 (acc, c);
+ if (unlikely (!cff2_plan.create (*this, c->plan))) return false;
+ return serialize (c->serializer, cff2_plan,
+ c->plan->normalized_coords.as_array ());
}
#endif
diff --git a/src/hb-subset-cff2.hh b/src/hb-subset-cff2.hh
deleted file mode 100644
index f10556d..0000000
--- a/src/hb-subset-cff2.hh
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright © 2018 Adobe Inc.
- *
- * This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Adobe Author(s): Michiharu Ariza
- */
-
-#ifndef HB_SUBSET_CFF2_HH
-#define HB_SUBSET_CFF2_HH
-
-#include "hb.hh"
-
-#include "hb-subset-plan.hh"
-
-HB_INTERNAL bool
-hb_subset_cff2 (hb_subset_context_t *c);
-
-#endif /* HB_SUBSET_CFF2_HH */
diff --git a/src/hb-subset-input.cc b/src/hb-subset-input.cc
index 465af50..e6b23df 100644
--- a/src/hb-subset-input.cc
+++ b/src/hb-subset-input.cc
@@ -438,7 +438,8 @@
if (!hb_ot_var_find_axis_info (face, axis_tag, &axis_info))
return false;
- return input->axes_location.set (axis_tag, axis_info.default_value);
+ float default_val = axis_info.default_value;
+ return input->axes_location.set (axis_tag, Triple (default_val, default_val, default_val));
}
/**
@@ -468,8 +469,52 @@
return false;
float val = hb_clamp(axis_value, axis_info.min_value, axis_info.max_value);
- return input->axes_location.set (axis_tag, val);
+ return input->axes_location.set (axis_tag, Triple (val, val, val));
}
+
+#ifdef HB_EXPERIMENTAL_API
+/**
+ * hb_subset_input_set_axis_range: (skip)
+ * @input: a #hb_subset_input_t object.
+ * @face: a #hb_face_t object.
+ * @axis_tag: Tag of the axis
+ * @axis_min_value: Minimum value of the axis variation range to set
+ * @axis_max_value: Maximum value of the axis variation range to set
+ *
+ * Restricting the range of variation on an axis in the given subset input object.
+ * New min/max values will be clamped if they're not within the fvar axis range.
+ * If the fvar axis default value is not within the new range, the new default
+ * value will be changed to the new min or max value, whichever is closer to the fvar
+ * axis default.
+ *
+ * Note: input min value can not be bigger than input max value
+ * Note: currently this API does not support changing axis limits yet.It'd be only
+ * used internally for setting axis limits in the internal data structures
+ *
+ * Return value: `true` if success, `false` otherwise
+ *
+ * XSince: EXPERIMENTAL
+ **/
+HB_EXTERN hb_bool_t
+hb_subset_input_set_axis_range (hb_subset_input_t *input,
+ hb_face_t *face,
+ hb_tag_t axis_tag,
+ float axis_min_value,
+ float axis_max_value)
+{
+ if (axis_min_value > axis_max_value)
+ return false;
+
+ hb_ot_var_axis_info_t axis_info;
+ if (!hb_ot_var_find_axis_info (face, axis_tag, &axis_info))
+ return false;
+
+ float new_min_val = hb_clamp(axis_min_value, axis_info.min_value, axis_info.max_value);
+ float new_max_val = hb_clamp(axis_max_value, axis_info.min_value, axis_info.max_value);
+ float new_default_val = hb_clamp(axis_info.default_value, new_min_val, new_max_val);
+ return input->axes_location.set (axis_tag, Triple (new_min_val, new_default_val, new_max_val));
+}
+#endif
#endif
/**
diff --git a/src/hb-subset-input.hh b/src/hb-subset-input.hh
index 1970f79..6ae311e 100644
--- a/src/hb-subset-input.hh
+++ b/src/hb-subset-input.hh
@@ -35,6 +35,7 @@
#include "hb-set.hh"
#include "hb-cplusplus.hh"
#include "hb-font.hh"
+#include "hb-subset-instancer-solver.hh"
struct hb_ot_name_record_ids_t
{
@@ -118,7 +119,7 @@
// If set loca format will always be the long version.
bool force_long_loca = false;
- hb_hashmap_t<hb_tag_t, float> axes_location;
+ hb_hashmap_t<hb_tag_t, Triple> axes_location;
hb_map_t glyph_map;
#ifdef HB_EXPERIMENTAL_API
hb_hashmap_t<hb_ot_name_record_ids_t, hb_bytes_t> name_table_overrides;
diff --git a/src/hb-subset-instancer-solver.cc b/src/hb-subset-instancer-solver.cc
index 7a2735c..c698d94 100644
--- a/src/hb-subset-instancer-solver.cc
+++ b/src/hb-subset-instancer-solver.cc
@@ -22,7 +22,7 @@
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*/
-#include "hb.hh"
+#include "hb-subset-instancer-solver.hh"
/* This file is a straight port of the following:
*
@@ -35,26 +35,6 @@
constexpr static float EPSILON = 1.f / (1 << 14);
constexpr static float MAX_F2DOT14 = float (0x7FFF) / (1 << 14);
-struct Triple {
-
- Triple () :
- minimum (0.f), middle (0.f), maximum (0.f) {}
-
- Triple (float minimum_, float middle_, float maximum_) :
- minimum (minimum_), middle (middle_), maximum (maximum_) {}
-
- bool operator == (const Triple &o) const
- {
- return minimum == o.minimum &&
- middle == o.middle &&
- maximum == o.maximum;
- }
-
- float minimum;
- float middle;
- float maximum;
-};
-
static inline Triple _reverse_negate(const Triple &v)
{ return {-v.maximum, -v.middle, -v.minimum}; }
@@ -82,10 +62,6 @@
return (end - coord) / (end - peak);
}
-
-using result_item_t = hb_pair_t<float, Triple>;
-using result_t = hb_vector_t<result_item_t>;
-
static inline result_t
_solve (Triple tent, Triple axisLimit, bool negative = false)
{
@@ -195,9 +171,9 @@
if (gain > outGain)
{
// Crossing point on the axis.
- float crossing = peak + ((1 - gain) * (upper - peak) / (1 - outGain));
+ float crossing = peak + (1 - gain) * (upper - peak);
- Triple loc{peak, peak, crossing};
+ Triple loc{axisDef, peak, crossing};
float scalar = 1.f;
// The part before the crossing point.
@@ -213,7 +189,7 @@
if (upper >= axisMax)
{
Triple loc {crossing, axisMax, axisMax};
- float scalar = supportScalar (axisMax, tent);
+ float scalar = outGain;
out.push (hb_pair (scalar - gain, loc));
}
@@ -247,89 +223,83 @@
// Eternity justify.
Triple loc2 {upper, axisMax, axisMax};
- float scalar2 = 1.f; // supportScalar({"tag": axisMax}, {"tag": tent})
+ float scalar2 = 0.f;
out.push (hb_pair (scalar1 - gain, loc1));
out.push (hb_pair (scalar2 - gain, loc2));
}
}
- /* Case 3: Outermost limit still fits within F2Dot14 bounds;
- * we keep deltas as is and only scale the axes bounds. Deltas beyond -1.0
- * or +1.0 will never be applied as implementations must clamp to that range.
- *
- * A second tent is needed for cases when gain is positive, though we add it
- * unconditionally and it will be dropped because scalar ends up 0.
- *
- * TODO: See if we can just move upper closer to adjust the slope, instead of
- * second tent.
- *
- * | peak |
- * 1.........|............o...|..................
- * | /x\ |
- * | /xxx\ |
- * | /xxxxx\|
- * | /xxxxxxx+
- * | /xxxxxxxx|\
- * 0---|-----|------oxxxxxxxxx|xo---------------1
- * axisMin | lower | upper
- * | |
- * axisDef axisMax
- */
- else if (axisDef + (axisMax - axisDef) * 2 >= upper)
+ else
{
- if (!negative && axisDef + (axisMax - axisDef) * MAX_F2DOT14 < upper)
- {
- // we clamp +2.0 to the max F2Dot14 (~1.99994) for convenience
- upper = axisDef + (axisMax - axisDef) * MAX_F2DOT14;
- assert (peak < upper);
- }
-
// Special-case if peak is at axisMax.
if (axisMax == peak)
upper = peak;
- Triple loc1 {hb_max (axisDef, lower), peak, upper};
- float scalar1 = 1.f;
+ /* Case 3:
+ * we keep deltas as is and only scale the axis upper to achieve
+ * the desired new tent if feasible.
+ *
+ * peak
+ * 1.....................o....................
+ * / \_|
+ * ..................../....+_.........outGain
+ * / | \
+ * gain..............+......|..+_.............
+ * /| | | \
+ * 0---|-----------o | | | o----------1
+ * axisMin lower| | | upper
+ * | | newUpper
+ * axisDef axisMax
+ */
+ float newUpper = peak + (1 - gain) * (upper - peak);
+ // I feel like the first condition is always true because
+ // outGain >= gain.
+ if (axisMax <= newUpper && newUpper <= axisDef + (axisMax - axisDef) * 2)
+ {
+ upper = newUpper;
+ if (!negative && axisDef + (axisMax - axisDef) * MAX_F2DOT14 < upper)
+ {
+ // we clamp +2.0 to the max F2Dot14 (~1.99994) for convenience
+ upper = axisDef + (axisMax - axisDef) * MAX_F2DOT14;
+ assert (peak < upper);
+ }
- Triple loc2 {peak, upper, upper};
- float scalar2 = 0.f;
+ Triple loc {hb_max (axisDef, lower), peak, upper};
+ float scalar = 1.f;
- // Don't add a dirac delta!
- if (axisDef < upper)
- out.push (hb_pair (scalar1 - gain, loc1));
- if (peak < upper)
+ out.push (hb_pair (scalar - gain, loc));
+ }
+
+ /* Case 4: New limit doesn't fit; we need to chop into two tents,
+ * because the shape of a triangle with part of one side cut off
+ * cannot be represented as a triangle itself.
+ *
+ * | peak |
+ * 1.........|......o.|....................
+ * ..........|...../x\|.............outGain
+ * | |xxy|\_
+ * | /xxxy| \_
+ * | |xxxxy| \_
+ * | /xxxxy| \_
+ * 0---|-----|-oxxxxxx| o----------1
+ * axisMin | lower | upper
+ * | |
+ * axisDef axisMax
+ */
+ else
+ {
+ Triple loc1 {hb_max (axisDef, lower), peak, axisMax};
+ float scalar1 = 1.f;
+
+ Triple loc2 {peak, axisMax, axisMax};
+ float scalar2 = outGain;
+
+ out.push (hb_pair (scalar1 - gain, loc1));
+ // Don't add a dirac delta!
+ if (peak < axisMax)
out.push (hb_pair (scalar2 - gain, loc2));
- }
-
- /* Case 4: New limit doesn't fit; we need to chop into two tents,
- * because the shape of a triangle with part of one side cut off
- * cannot be represented as a triangle itself.
- *
- * | peak |
- * 1.........|......o.|...................
- * | /x\|
- * | |xxy|\_
- * | /xxxy| \_
- * | |xxxxy| \_
- * | /xxxxy| \_
- * 0---|-----|-oxxxxxx| o----------1
- * axisMin | lower | upper
- * | |
- * axisDef axisMax
- */
- else
- {
- Triple loc1 {hb_max (axisDef, lower), peak, axisMax};
- float scalar1 = 1.f;
-
- Triple loc2 {peak, axisMax, axisMax};
- float scalar2 = supportScalar (axisMax, tent);
-
- out.push (hb_pair (scalar1 - gain, loc1));
- // Don't add a dirac delta!
- if (peak < axisMax)
- out.push (hb_pair (scalar2 - gain, loc2));
+ }
}
/* Now, the negative side
@@ -422,19 +392,6 @@
}
}
-/* Given a tuple (lower,peak,upper) "tent" and new axis limits
- * (axisMin,axisDefault,axisMax), solves how to represent the tent
- * under the new axis configuration. All values are in normalized
- * -1,0,+1 coordinate system. Tent values can be outside this range.
- *
- * Return value: a list of tuples. Each tuple is of the form
- * (scalar,tent), where scalar is a multipler to multiply any
- * delta-sets by, and tent is a new tent for that output delta-set.
- * If tent value is Triple{}, that is a special deltaset that should
- * be always-enabled (called "gain").
- */
-HB_INTERNAL result_t rebase_tent (Triple tent, Triple axisLimit);
-
result_t
rebase_tent (Triple tent, Triple axisLimit)
{
@@ -460,5 +417,5 @@
Triple{n (t.minimum), n (t.middle), n (t.maximum)}));
}
- return sols;
+ return out;
}
diff --git a/src/hb-subset-instancer-solver.hh b/src/hb-subset-instancer-solver.hh
new file mode 100644
index 0000000..b1f8594
--- /dev/null
+++ b/src/hb-subset-instancer-solver.hh
@@ -0,0 +1,90 @@
+/*
+ * Copyright © 2023 Behdad Esfahbod
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef HB_SUBSET_INSTANCER_SOLVER_HH
+#define HB_SUBSET_INSTANCER_SOLVER_HH
+
+#include "hb.hh"
+
+struct Triple {
+
+ Triple () :
+ minimum (0.f), middle (0.f), maximum (0.f) {}
+
+ Triple (float minimum_, float middle_, float maximum_) :
+ minimum (minimum_), middle (middle_), maximum (maximum_) {}
+
+ bool operator == (const Triple &o) const
+ {
+ return minimum == o.minimum &&
+ middle == o.middle &&
+ maximum == o.maximum;
+ }
+
+ bool operator != (const Triple o) const
+ { return !(*this == o); }
+
+ bool is_point () const
+ { return minimum == middle && middle == maximum; }
+
+ bool contains (float point) const
+ { return minimum <= point && point <= maximum; }
+
+ /* from hb_array_t hash ()*/
+ uint32_t hash () const
+ {
+ uint32_t current = /*cbf29ce4*/0x84222325;
+ current = current ^ hb_hash (minimum);
+ current = current * 16777619;
+
+ current = current ^ hb_hash (middle);
+ current = current * 16777619;
+
+ current = current ^ hb_hash (maximum);
+ current = current * 16777619;
+ return current;
+ }
+
+ float minimum;
+ float middle;
+ float maximum;
+};
+
+using result_item_t = hb_pair_t<float, Triple>;
+using result_t = hb_vector_t<result_item_t>;
+
+/* Given a tuple (lower,peak,upper) "tent" and new axis limits
+ * (axisMin,axisDefault,axisMax), solves how to represent the tent
+ * under the new axis configuration. All values are in normalized
+ * -1,0,+1 coordinate system. Tent values can be outside this range.
+ *
+ * Return value: a list of tuples. Each tuple is of the form
+ * (scalar,tent), where scalar is a multipler to multiply any
+ * delta-sets by, and tent is a new tent for that output delta-set.
+ * If tent value is Triple{}, that is a special deltaset that should
+ * be always-enabled (called "gain").
+ */
+HB_INTERNAL result_t rebase_tent (Triple tent, Triple axisLimit);
+
+#endif /* HB_SUBSET_INSTANCER_SOLVER_HH */
diff --git a/src/hb-subset-plan-member-list.hh b/src/hb-subset-plan-member-list.hh
index acf508c..be29e67 100644
--- a/src/hb-subset-plan-member-list.hh
+++ b/src/hb-subset-plan-member-list.hh
@@ -33,7 +33,9 @@
// For each cp that we'd like to retain maps to the corresponding gid.
HB_SUBSET_PLAN_MEMBER (hb_set_t, unicodes)
-HB_SUBSET_PLAN_MEMBER (hb_sorted_vector_t E(<hb_pair_t<hb_codepoint_t, hb_codepoint_t>>), unicode_to_new_gid_list)
+HB_SUBSET_PLAN_MEMBER (hb_sorted_vector_t<hb_codepoint_pair_t>, unicode_to_new_gid_list)
+
+HB_SUBSET_PLAN_MEMBER (hb_sorted_vector_t<hb_codepoint_pair_t>, new_to_old_gid_list)
// name_ids we would like to retain
HB_SUBSET_PLAN_MEMBER (hb_set_t, name_ids)
@@ -97,12 +99,12 @@
HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E(<hb_tag_t, hb::unique_ptr<hb_blob_t>>), sanitized_table_cache)
-//normalized axes location map
-HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E(<hb_tag_t, int>), axes_location)
+//normalized axes range map
+HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E(<hb_tag_t, Triple>), axes_location)
HB_SUBSET_PLAN_MEMBER (hb_vector_t<int>, normalized_coords)
-//user specified axes location map
-HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E(<hb_tag_t, float>), user_axes_location)
+//user specified axes range map
+HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E(<hb_tag_t, Triple>), user_axes_location)
//retained old axis index -> new axis index mapping in fvar axis array
HB_SUBSET_PLAN_MEMBER (hb_map_t, axes_index_map)
@@ -115,9 +117,9 @@
//vmtx metrics map: new gid->(advance, lsb)
HB_SUBSET_PLAN_MEMBER (mutable hb_hashmap_t E(<hb_codepoint_t, hb_pair_t E(<unsigned, int>)>), vmtx_map)
//boundsWidth map: new gid->boundsWidth, boundWidth=xMax - xMin
-HB_SUBSET_PLAN_MEMBER (mutable hb_map_t, bounds_width_map)
+HB_SUBSET_PLAN_MEMBER (mutable hb_vector_t<unsigned>, bounds_width_vec)
//boundsHeight map: new gid->boundsHeight, boundsHeight=yMax - yMin
-HB_SUBSET_PLAN_MEMBER (mutable hb_map_t, bounds_height_map)
+HB_SUBSET_PLAN_MEMBER (mutable hb_vector_t<unsigned>, bounds_height_vec)
#ifdef HB_EXPERIMENTAL_API
// name table overrides map: hb_ot_name_record_ids_t-> name string new value or
diff --git a/src/hb-subset-plan.cc b/src/hb-subset-plan.cc
index 791f92d..9a00de3 100644
--- a/src/hb-subset-plan.cc
+++ b/src/hb-subset-plan.cc
@@ -48,10 +48,24 @@
using OT::Layout::GSUB;
using OT::Layout::GPOS;
+
+hb_subset_accelerator_t::~hb_subset_accelerator_t ()
+{
+ if (cmap_cache && destroy_cmap_cache)
+ destroy_cmap_cache ((void*) cmap_cache);
+
+#ifndef HB_NO_SUBSET_CFF
+ cff1_accel.fini ();
+ cff2_accel.fini ();
+#endif
+ hb_face_destroy (source);
+}
+
+
typedef hb_hashmap_t<unsigned, hb::unique_ptr<hb_set_t>> script_langsys_map;
#ifndef HB_NO_SUBSET_CFF
static inline bool
-_add_cff_seac_components (const OT::cff1::accelerator_t &cff,
+_add_cff_seac_components (const OT::cff1::accelerator_subset_t &cff,
hb_codepoint_t gid,
hb_set_t *gids_to_retain)
{
@@ -135,7 +149,8 @@
hb_set_t *lookup_indices, /* OUT */
hb_set_t *feature_indices, /* OUT */
hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *feature_record_cond_idx_map, /* OUT */
- hb_hashmap_t<unsigned, const OT::Feature*> *feature_substitutes_map /* OUT */)
+ hb_hashmap_t<unsigned, const OT::Feature*> *feature_substitutes_map, /* OUT */
+ bool& insert_catch_all_feature_variation_record)
{
unsigned num_features = table.get_feature_count ();
hb_vector_t<hb_tag_t> features;
@@ -171,8 +186,11 @@
&plan->axes_location,
feature_record_cond_idx_map,
feature_substitutes_map,
+ insert_catch_all_feature_variation_record,
feature_indices,
- true,
+ false,
+ false,
+ false,
0,
&conditionset_map
};
@@ -283,7 +301,8 @@
hb_map_t *features,
script_langsys_map *langsys_map,
hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *feature_record_cond_idx_map,
- hb_hashmap_t<unsigned, const OT::Feature*> *feature_substitutes_map)
+ hb_hashmap_t<unsigned, const OT::Feature*> *feature_substitutes_map,
+ bool& insert_catch_all_feature_variation_record)
{
hb_blob_ptr_t<T> table = plan->source_table<T> ();
hb_tag_t table_tag = table->tableTag;
@@ -293,7 +312,8 @@
&lookup_indices,
&feature_indices,
feature_record_cond_idx_map,
- feature_substitutes_map);
+ feature_substitutes_map,
+ insert_catch_all_feature_variation_record);
if (table_tag == HB_OT_TAG_GSUB && !(plan->flags & HB_SUBSET_FLAGS_NO_LAYOUT_CLOSURE))
hb_ot_layout_lookups_substitute_closure (plan->source,
@@ -329,7 +349,7 @@
{
if (varidx_set.is_empty () || subtable_count == 0) return;
- inner_maps.resize (subtable_count);
+ if (unlikely (!inner_maps.resize (subtable_count))) return;
for (unsigned idx : varidx_set)
{
uint16_t major = idx >> 16;
@@ -356,7 +376,7 @@
{
hb_variation_t var;
var.tag = _.first;
- var.value = _.second;
+ var.value = _.second.middle;
vars.push (var);
}
@@ -541,6 +561,8 @@
unicodes->get_population () < cmap_unicodes->get_population () &&
glyphs->get_population () < cmap_unicodes->get_population ())
{
+ plan->codepoint_to_glyph->alloc (unicodes->get_population () + glyphs->get_population ());
+
auto &gid_to_unicodes = plan->accelerator->gid_to_unicodes;
for (hb_codepoint_t gid : *glyphs)
{
@@ -569,6 +591,7 @@
}
else
{
+ plan->codepoint_to_glyph->alloc (cmap_unicodes->get_population ());
for (hb_codepoint_t cp : *cmap_unicodes)
{
hb_codepoint_t gid = (*unicode_glyphid_map)[cp];
@@ -581,9 +604,10 @@
}
/* Add gids which where requested, but not mapped in cmap */
+ unsigned num_glyphs = plan->source->get_num_glyphs ();
for (hb_codepoint_t gid : *glyphs)
{
- if (gid >= plan->source->get_num_glyphs ())
+ if (gid >= num_glyphs)
break;
plan->_glyphset_gsub.add (gid);
}
@@ -616,7 +640,9 @@
if (unlikely (depth++ > HB_MAX_NESTING_LEVEL)) return operation_count;
if (unlikely (--operation_count < 0)) return operation_count;
- for (auto &item : glyf.glyph_for_gid (gid).get_composite_iterator ())
+ auto glyph = glyf.glyph_for_gid (gid);
+
+ for (auto &item : glyph.get_composite_iterator ())
operation_count =
_glyf_add_gid_and_children (glyf,
item.get_gid (),
@@ -625,7 +651,7 @@
depth);
#ifndef HB_NO_VAR_COMPOSITES
- for (auto &item : glyf.glyph_for_gid (gid).get_var_composite_iterator ())
+ for (auto &item : glyph.get_var_composite_iterator ())
{
operation_count =
_glyf_add_gid_and_children (glyf,
@@ -648,7 +674,7 @@
#endif
#ifndef HB_NO_VAR
if (!plan->all_axes_pinned)
- plan->source->table.fvar->collect_name_ids (&plan->user_axes_location, &plan->name_ids);
+ plan->source->table.fvar->collect_name_ids (&plan->user_axes_location, &plan->axes_old_index_tag_map, &plan->name_ids);
#endif
#ifndef HB_NO_COLOR
if (!drop_tables->has (HB_OT_TAG_CPAL))
@@ -677,7 +703,11 @@
{
OT::glyf_accelerator_t glyf (plan->source);
#ifndef HB_NO_SUBSET_CFF
- OT::cff1::accelerator_t cff (plan->source);
+ // Note: we cannot use inprogress_accelerator here, since it has not been
+ // created yet. So in case of preprocessed-face (and otherwise), we do an
+ // extra sanitize pass here, which is not ideal.
+ OT::cff1::accelerator_subset_t stack_cff (plan->accelerator ? nullptr : plan->source);
+ const OT::cff1::accelerator_subset_t *cff (plan->accelerator ? plan->accelerator->cff1_accel.get () : &stack_cff);
#endif
plan->_glyphset_gsub.add (0); // Not-def
@@ -694,7 +724,8 @@
&plan->gsub_features,
&plan->gsub_langsys,
&plan->gsub_feature_record_cond_idx_map,
- &plan->gsub_feature_substitutes_map);
+ &plan->gsub_feature_substitutes_map,
+ plan->gsub_insert_catch_all_feature_variation_rec);
if (!drop_tables->has (HB_OT_TAG_GPOS))
_closure_glyphs_lookups_features<GPOS> (
@@ -704,7 +735,8 @@
&plan->gpos_features,
&plan->gpos_langsys,
&plan->gpos_feature_record_cond_idx_map,
- &plan->gpos_feature_substitutes_map);
+ &plan->gpos_feature_substitutes_map,
+ plan->gpos_insert_catch_all_feature_variation_rec);
#endif
_remove_invalid_gids (&plan->_glyphset_gsub, plan->source->get_num_glyphs ());
@@ -737,9 +769,9 @@
if (!plan->accelerator || plan->accelerator->has_seac)
{
bool has_seac = false;
- if (cff.is_valid ())
+ if (cff->is_valid ())
for (hb_codepoint_t gid : cur_glyphset)
- if (_add_cff_seac_components (cff, gid, &plan->_glyphset))
+ if (_add_cff_seac_components (*cff, gid, &plan->_glyphset))
has_seac = true;
plan->has_seac = has_seac;
}
@@ -747,7 +779,6 @@
_remove_invalid_gids (&plan->_glyphset, plan->source->get_num_glyphs ());
-
#ifndef HB_NO_VAR
if (!drop_tables->has (HB_OT_TAG_GDEF))
_collect_layout_variation_indices (plan);
@@ -759,10 +790,10 @@
const hb_map_t* glyph_map,
hb_map_t* out)
{
+ out->alloc (glyph_set_gsub->get_population ());
+ hb_iter (glyph_set_gsub)
| hb_map ([&] (hb_codepoint_t gid) {
- return hb_pair_t<hb_codepoint_t, hb_codepoint_t> (gid,
- glyph_map->get (gid));
+ return hb_codepoint_pair_t (gid, glyph_map->get (gid));
})
| hb_sink (out)
;
@@ -775,11 +806,13 @@
const hb_map_t *requested_glyph_map,
hb_map_t *glyph_map, /* OUT */
hb_map_t *reverse_glyph_map, /* OUT */
+ hb_sorted_vector_t<hb_codepoint_pair_t> *new_to_old_gid_list /* OUT */,
unsigned int *num_glyphs /* OUT */)
{
unsigned pop = all_gids_to_retain->get_population ();
- reverse_glyph_map->resize (pop);
- glyph_map->resize (pop);
+ reverse_glyph_map->alloc (pop);
+ glyph_map->alloc (pop);
+ new_to_old_gid_list->alloc (pop);
if (*requested_glyph_map)
{
@@ -803,45 +836,44 @@
for (auto old_gid : all_gids_to_retain->iter ())
{
if (old_gid == 0) {
- reverse_glyph_map->set(0, 0);
+ new_to_old_gid_list->push (hb_pair<hb_codepoint_t, hb_codepoint_t> (0u, 0u));
continue;
}
hb_codepoint_t* new_gid;
if (!requested_glyph_map->has (old_gid, &new_gid))
{
- remaining.add(old_gid);
+ remaining.add(old_gid);
continue;
}
if (*new_gid > max_glyph)
max_glyph = *new_gid;
- reverse_glyph_map->set (*new_gid, old_gid);
+ new_to_old_gid_list->push (hb_pair (*new_gid, old_gid));
}
+ new_to_old_gid_list->qsort ();
// Anything that wasn't mapped by the requested mapping should
// be placed after the requested mapping.
for (auto old_gid : remaining)
- {
- reverse_glyph_map->set(++max_glyph, old_gid);
- }
+ new_to_old_gid_list->push (hb_pair (++max_glyph, old_gid));
*num_glyphs = max_glyph + 1;
}
else if (!retain_gids)
{
+ hb_enumerate (hb_iter (all_gids_to_retain), (hb_codepoint_t) 0)
- | hb_sink (reverse_glyph_map)
+ | hb_sink (new_to_old_gid_list)
;
- *num_glyphs = reverse_glyph_map->get_population ();
+ *num_glyphs = new_to_old_gid_list->length;
}
else
{
+ hb_iter (all_gids_to_retain)
| hb_map ([] (hb_codepoint_t _) {
- return hb_pair_t<hb_codepoint_t, hb_codepoint_t> (_, _);
+ return hb_codepoint_pair_t (_, _);
})
- | hb_sink (reverse_glyph_map)
+ | hb_sink (new_to_old_gid_list)
;
hb_codepoint_t max_glyph = HB_SET_VALUE_INVALID;
@@ -850,8 +882,11 @@
*num_glyphs = max_glyph + 1;
}
- + reverse_glyph_map->iter ()
- | hb_map (&hb_pair_t<hb_codepoint_t, hb_codepoint_t>::reverse)
+ + hb_iter (new_to_old_gid_list)
+ | hb_sink (reverse_glyph_map)
+ ;
+ + hb_iter (new_to_old_gid_list)
+ | hb_map (&hb_codepoint_pair_t::reverse)
| hb_sink (glyph_map)
;
@@ -884,24 +919,35 @@
hb_tag_t axis_tag = axis.get_axis_tag ();
plan->axes_old_index_tag_map.set (old_axis_idx, axis_tag);
- if (!plan->user_axes_location.has (axis_tag))
+ if (!plan->user_axes_location.has (axis_tag) ||
+ !plan->user_axes_location.get (axis_tag).is_point ())
{
axis_not_pinned = true;
plan->axes_index_map.set (old_axis_idx, new_axis_idx);
new_axis_idx++;
}
- else
+
+ if (plan->user_axes_location.has (axis_tag))
{
- int normalized_v = axis.normalize_axis_value (plan->user_axes_location.get (axis_tag));
+ Triple axis_range = plan->user_axes_location.get (axis_tag);
+ int normalized_min = axis.normalize_axis_value (axis_range.minimum);
+ int normalized_default = axis.normalize_axis_value (axis_range.middle);
+ int normalized_max = axis.normalize_axis_value (axis_range.maximum);
+
if (has_avar && old_axis_idx < avar_axis_count)
{
- normalized_v = seg_maps->map (normalized_v);
+ normalized_min = seg_maps->map (normalized_min);
+ normalized_default = seg_maps->map (normalized_default);
+ normalized_max = seg_maps->map (normalized_max);
}
- plan->axes_location.set (axis_tag, normalized_v);
- if (normalized_v != 0)
+ plan->axes_location.set (axis_tag, Triple (static_cast<float> (normalized_min),
+ static_cast<float> (normalized_default),
+ static_cast<float> (normalized_max)));
+
+ if (normalized_default != 0)
plan->pinned_at_default = false;
- plan->normalized_coords[old_axis_idx] = normalized_v;
+ plan->normalized_coords[old_axis_idx] = normalized_default;
}
old_axis_idx++;
@@ -968,7 +1014,7 @@
continue;
}
plan->hmtx_map.set (new_gid, hb_pair ((unsigned) hori_aw, lsb));
- plan->bounds_width_map.set (new_gid, extents.width);
+ plan->bounds_width_vec[new_gid] = extents.width;
}
if (_vmtx.has_data ())
@@ -985,7 +1031,7 @@
continue;
}
plan->vmtx_map.set (new_gid, hb_pair ((unsigned) vert_aw, tsb));
- plan->bounds_height_map.set (new_gid, extents.height);
+ plan->bounds_height_vec[new_gid] = extents.height;
}
}
hb_font_destroy (font);
@@ -1018,6 +1064,8 @@
glyph_map = hb_map_create ();
reverse_glyph_map = hb_map_create ();
+ gsub_insert_catch_all_feature_variation_rec = false;
+ gpos_insert_catch_all_feature_variation_rec = false;
gdef_varstore_inner_maps.init ();
user_axes_location = input->axes_location;
@@ -1065,6 +1113,7 @@
&input->glyph_map,
glyph_map,
reverse_glyph_map,
+ &new_to_old_gid_list,
&_num_output_glyphs))) {
return;
}
@@ -1082,6 +1131,13 @@
glyph_map->get(unicode_to_new_gid_list.arrayZ[i].second);
}
+ bounds_width_vec.resize (_num_output_glyphs, false);
+ for (auto &v : bounds_width_vec)
+ v = 0xFFFFFFFF;
+ bounds_height_vec.resize (_num_output_glyphs, false);
+ for (auto &v : bounds_height_vec)
+ v = 0xFFFFFFFF;
+
if (unlikely (in_error ()))
return;
@@ -1091,19 +1147,9 @@
if (attach_accelerator_data)
{
- hb_multimap_t gid_to_unicodes;
-
- hb_map_t &unicode_to_gid = *codepoint_to_glyph;
-
- for (auto unicode : unicodes)
- {
- auto gid = unicode_to_gid[unicode];
- gid_to_unicodes.add (gid, unicode);
- }
-
inprogress_accelerator =
- hb_subset_accelerator_t::create (*codepoint_to_glyph,
- gid_to_unicodes,
+ hb_subset_accelerator_t::create (source,
+ *codepoint_to_glyph,
unicodes,
has_seac);
@@ -1115,6 +1161,29 @@
#undef HB_SUBSET_PLAN_MEMBER
}
+hb_subset_plan_t::~hb_subset_plan_t()
+{
+ hb_face_destroy (dest);
+
+ hb_map_destroy (codepoint_to_glyph);
+ hb_map_destroy (glyph_map);
+ hb_map_destroy (reverse_glyph_map);
+#ifndef HB_NO_SUBSET_CFF
+ cff1_accel.fini ();
+ cff2_accel.fini ();
+#endif
+ hb_face_destroy (source);
+
+#ifdef HB_EXPERIMENTAL_API
+ for (auto _ : name_table_overrides.iter_ref ())
+ _.second.fini ();
+#endif
+
+ if (inprogress_accelerator)
+ hb_subset_accelerator_t::destroy ((void*) inprogress_accelerator);
+}
+
+
/**
* hb_subset_plan_create_or_fail:
* @face: font face to create the plan for.
diff --git a/src/hb-subset-plan.hh b/src/hb-subset-plan.hh
index 19470ff..d156de0 100644
--- a/src/hb-subset-plan.hh
+++ b/src/hb-subset-plan.hh
@@ -67,28 +67,17 @@
typedef struct head_maxp_info_t head_maxp_info_t;
+namespace OT {
+ struct cff1_subset_accelerator_t;
+ struct cff2_subset_accelerator_t;
+}
+
struct hb_subset_plan_t
{
HB_INTERNAL hb_subset_plan_t (hb_face_t *,
const hb_subset_input_t *input);
- ~hb_subset_plan_t()
- {
- hb_face_destroy (source);
- hb_face_destroy (dest);
-
- hb_map_destroy (codepoint_to_glyph);
- hb_map_destroy (glyph_map);
- hb_map_destroy (reverse_glyph_map);
-
-#ifdef HB_EXPERIMENTAL_API
- for (auto _ : name_table_overrides)
- _.second.fini ();
-#endif
-
- if (inprogress_accelerator)
- hb_subset_accelerator_t::destroy ((void*) inprogress_accelerator);
- }
+ HB_INTERNAL ~hb_subset_plan_t();
hb_object_header_t header;
@@ -106,6 +95,12 @@
// Plan is only good for a specific source/dest so keep them with it
hb_face_t *source;
+#ifndef HB_NO_SUBSET_CFF
+ // These have to be immediately after source:
+ hb_face_lazy_loader_t<OT::cff1_subset_accelerator_t, 1> cff1_accel;
+ hb_face_lazy_loader_t<OT::cff2_subset_accelerator_t, 2> cff2_accel;
+#endif
+
hb_face_t *dest;
unsigned int _num_output_glyphs;
@@ -114,6 +109,10 @@
bool pinned_at_default;
bool has_seac;
+ // whether to insert a catch-all FeatureVariationRecord
+ bool gsub_insert_catch_all_feature_variation_rec;
+ bool gpos_insert_catch_all_feature_variation_rec;
+
#define HB_SUBSET_PLAN_MEMBER(Type, Name) Type Name;
#include "hb-subset-plan-member-list.hh"
#undef HB_SUBSET_PLAN_MEMBER
@@ -127,25 +126,31 @@
public:
template<typename T>
- hb_blob_ptr_t<T> source_table()
+ struct source_table_loader
{
- hb_lock_t lock (accelerator ? &accelerator->sanitized_table_cache_lock : nullptr);
+ hb_blob_ptr_t<T> operator () (hb_subset_plan_t *plan)
+ {
+ hb_lock_t lock (plan->accelerator ? &plan->accelerator->sanitized_table_cache_lock : nullptr);
- auto *cache = accelerator ? &accelerator->sanitized_table_cache : &sanitized_table_cache;
- if (cache
- && !cache->in_error ()
- && cache->has (+T::tableTag)) {
- return hb_blob_reference (cache->get (+T::tableTag).get ());
+ auto *cache = plan->accelerator ? &plan->accelerator->sanitized_table_cache : &plan->sanitized_table_cache;
+ if (cache
+ && !cache->in_error ()
+ && cache->has (+T::tableTag)) {
+ return hb_blob_reference (cache->get (+T::tableTag).get ());
+ }
+
+ hb::unique_ptr<hb_blob_t> table_blob {hb_sanitize_context_t ().reference_table<T> (plan->source)};
+ hb_blob_t* ret = hb_blob_reference (table_blob.get ());
+
+ if (likely (cache))
+ cache->set (+T::tableTag, std::move (table_blob));
+
+ return ret;
}
+ };
- hb::unique_ptr<hb_blob_t> table_blob {hb_sanitize_context_t ().reference_table<T> (source)};
- hb_blob_t* ret = hb_blob_reference (table_blob.get ());
-
- if (likely (cache))
- cache->set (+T::tableTag, std::move (table_blob));
-
- return ret;
- }
+ template<typename T>
+ auto source_table() HB_AUTO_RETURN (source_table_loader<T> {} (this))
bool in_error () const { return !successful; }
@@ -184,15 +189,6 @@
return _num_output_glyphs;
}
- /*
- * Given an output gid , returns true if that glyph id is an empty
- * glyph (ie. it's a gid that we are dropping all data for).
- */
- inline bool is_empty_glyph (hb_codepoint_t gid) const
- {
- return !_glyphset.has (gid);
- }
-
inline bool new_gid_for_codepoint (hb_codepoint_t codepoint,
hb_codepoint_t *new_gid) const
{
@@ -242,4 +238,5 @@
}
};
+
#endif /* HB_SUBSET_PLAN_HH */
diff --git a/src/hb-subset.cc b/src/hb-subset.cc
index 9c066e6..f497700 100644
--- a/src/hb-subset.cc
+++ b/src/hb-subset.cc
@@ -62,6 +62,27 @@
using OT::Layout::GSUB;
using OT::Layout::GPOS;
+
+#ifndef HB_NO_SUBSET_CFF
+template<>
+struct hb_subset_plan_t::source_table_loader<const OT::cff1>
+{
+ auto operator () (hb_subset_plan_t *plan)
+ HB_AUTO_RETURN (plan->accelerator ? plan->accelerator->cff1_accel :
+ plan->inprogress_accelerator ? plan->inprogress_accelerator->cff1_accel :
+ plan->cff1_accel)
+};
+template<>
+struct hb_subset_plan_t::source_table_loader<const OT::cff2>
+{
+ auto operator () (hb_subset_plan_t *plan)
+ HB_AUTO_RETURN (plan->accelerator ? plan->accelerator->cff2_accel :
+ plan->inprogress_accelerator ? plan->inprogress_accelerator->cff2_accel :
+ plan->cff2_accel)
+};
+#endif
+
+
/**
* SECTION:hb-subset
* @title: hb-subset
@@ -192,15 +213,36 @@
static unsigned
_plan_estimate_subset_table_size (hb_subset_plan_t *plan,
unsigned table_len,
- bool same_size)
+ hb_tag_t table_tag)
{
unsigned src_glyphs = plan->source->get_num_glyphs ();
unsigned dst_glyphs = plan->glyphset ()->get_population ();
- if (unlikely (!src_glyphs) || same_size)
- return 512 + table_len;
+ unsigned bulk = 8192;
+ /* Tables that we want to allocate same space as the source table. For GSUB/GPOS it's
+ * because those are expensive to subset, so giving them more room is fine. */
+ bool same_size = table_tag == HB_OT_TAG_GSUB ||
+ table_tag == HB_OT_TAG_GPOS ||
+ table_tag == HB_OT_TAG_name;
- return 512 + (unsigned) (table_len * sqrt ((double) dst_glyphs / src_glyphs));
+ if (plan->flags & HB_SUBSET_FLAGS_RETAIN_GIDS)
+ {
+ if (table_tag == HB_OT_TAG_CFF1)
+ {
+ /* Add some extra room for the CFF charset. */
+ bulk += src_glyphs * 16;
+ }
+ else if (table_tag == HB_OT_TAG_CFF2)
+ {
+ /* Just extra CharString offsets. */
+ bulk += src_glyphs * 4;
+ }
+ }
+
+ if (unlikely (!src_glyphs) || same_size)
+ return bulk + table_len;
+
+ return bulk + (unsigned) (table_len * sqrt ((double) dst_glyphs / src_glyphs));
}
/*
@@ -231,7 +273,7 @@
hb_vector_t<char>* buf,
hb_subset_context_t* c /* OUT */)
{
- c->serializer->start_serialize<TableType> ();
+ c->serializer->start_serialize ();
if (c->serializer->in_error ()) return false;
bool needed = table->subset (c);
@@ -262,45 +304,46 @@
return _try_subset (table, buf, c);
}
+template <typename T>
+static auto _do_destroy (T &t, hb_priority<1>) HB_RETURN (void, t.destroy ())
+
+template <typename T>
+static void _do_destroy (T &t, hb_priority<0>) {}
+
template<typename TableType>
static bool
_subset (hb_subset_plan_t *plan, hb_vector_t<char> &buf)
{
- hb_blob_ptr_t<TableType> source_blob = plan->source_table<TableType> ();
- const TableType *table = source_blob.get ();
+ auto &&source_blob = plan->source_table<TableType> ();
+ auto *table = source_blob.get ();
hb_tag_t tag = TableType::tableTag;
- if (!source_blob.get_blob()->data)
+ hb_blob_t *blob = source_blob.get_blob();
+ if (unlikely (!blob || !blob->data))
{
DEBUG_MSG (SUBSET, nullptr,
"OT::%c%c%c%c::subset sanitize failed on source table.", HB_UNTAG (tag));
- source_blob.destroy ();
+ _do_destroy (source_blob, hb_prioritize);
return false;
}
- /* Tables that we want to allocate same space as the source table. For GSUB/GPOS it's
- * because those are expensive to subset, so giving them more room is fine. */
- bool same_size_table = TableType::tableTag == HB_OT_TAG_GSUB ||
- TableType::tableTag == HB_OT_TAG_GPOS ||
- TableType::tableTag == HB_OT_TAG_name;
-
- unsigned buf_size = _plan_estimate_subset_table_size (plan, source_blob.get_length (), same_size_table);
+ unsigned buf_size = _plan_estimate_subset_table_size (plan, blob->length, TableType::tableTag);
DEBUG_MSG (SUBSET, nullptr,
"OT::%c%c%c%c initial estimated table size: %u bytes.", HB_UNTAG (tag), buf_size);
if (unlikely (!buf.alloc (buf_size)))
{
DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c failed to allocate %u bytes.", HB_UNTAG (tag), buf_size);
- source_blob.destroy ();
+ _do_destroy (source_blob, hb_prioritize);
return false;
}
bool needed = false;
hb_serialize_context_t serializer (buf.arrayZ, buf.allocated);
{
- hb_subset_context_t c (source_blob.get_blob (), plan, &serializer, tag);
+ hb_subset_context_t c (blob, plan, &serializer, tag);
needed = _try_subset (table, &buf, &c);
}
- source_blob.destroy ();
+ _do_destroy (source_blob, hb_prioritize);
if (serializer.in_error () && !serializer.only_offset_overflow ())
{
@@ -587,46 +630,49 @@
offset += num_tables;
}
- hb_vector_t<char> buf;
- buf.alloc (4096 - 16);
-
-
bool success = true;
- while (!pending_subset_tags.is_empty ())
{
- if (subsetted_tags.in_error ()
- || pending_subset_tags.in_error ()) {
- success = false;
- goto end;
- }
+ // Grouping to deallocate buf before calling hb_face_reference (plan->dest).
- bool made_changes = false;
- for (hb_tag_t tag : pending_subset_tags)
+ hb_vector_t<char> buf;
+ buf.alloc (8192 - 16);
+
+ while (!pending_subset_tags.is_empty ())
{
- if (!_dependencies_satisfied (plan, tag,
- subsetted_tags,
- pending_subset_tags))
- {
- // delayed subsetting for some tables since they might have dependency on other tables
- // in some cases: e.g: during instantiating glyf tables, hmetrics/vmetrics are updated
- // and saved in subset plan, hmtx/vmtx subsetting need to use these updated metrics values
- continue;
+ if (subsetted_tags.in_error ()
+ || pending_subset_tags.in_error ()) {
+ success = false;
+ goto end;
}
- pending_subset_tags.del (tag);
- subsetted_tags.add (tag);
- made_changes = true;
+ bool made_changes = false;
+ for (hb_tag_t tag : pending_subset_tags)
+ {
+ if (!_dependencies_satisfied (plan, tag,
+ subsetted_tags,
+ pending_subset_tags))
+ {
+ // delayed subsetting for some tables since they might have dependency on other tables
+ // in some cases: e.g: during instantiating glyf tables, hmetrics/vmetrics are updated
+ // and saved in subset plan, hmtx/vmtx subsetting need to use these updated metrics values
+ continue;
+ }
- success = _subset_table (plan, buf, tag);
- if (unlikely (!success)) goto end;
- }
+ pending_subset_tags.del (tag);
+ subsetted_tags.add (tag);
+ made_changes = true;
- if (!made_changes)
- {
- DEBUG_MSG (SUBSET, nullptr, "Table dependencies unable to be satisfied. Subset failed.");
- success = false;
- goto end;
+ success = _subset_table (plan, buf, tag);
+ if (unlikely (!success)) goto end;
+ }
+
+ if (!made_changes)
+ {
+ DEBUG_MSG (SUBSET, nullptr, "Table dependencies unable to be satisfied. Subset failed.");
+ success = false;
+ goto end;
+ }
}
}
diff --git a/src/hb-subset.h b/src/hb-subset.h
index 6368ff9..93f1f7f 100644
--- a/src/hb-subset.h
+++ b/src/hb-subset.h
@@ -177,6 +177,13 @@
#ifdef HB_EXPERIMENTAL_API
HB_EXTERN hb_bool_t
+hb_subset_input_set_axis_range (hb_subset_input_t *input,
+ hb_face_t *face,
+ hb_tag_t axis_tag,
+ float axis_min_value,
+ float axis_max_value);
+
+HB_EXTERN hb_bool_t
hb_subset_input_override_name_table (hb_subset_input_t *input,
hb_ot_name_id_t name_id,
unsigned platform_id,
diff --git a/src/hb-vector.hh b/src/hb-vector.hh
index d61ce48..23a96d7 100644
--- a/src/hb-vector.hh
+++ b/src/hb-vector.hh
@@ -54,7 +54,7 @@
hb_vector_t (const Iterable &o) : hb_vector_t ()
{
auto iter = hb_iter (o);
- if (iter.is_random_access_iterator)
+ if (iter.is_random_access_iterator || iter.has_fast_len)
alloc (hb_len (iter), true);
hb_copy (iter, *this);
}
@@ -62,7 +62,19 @@
{
alloc (o.length, true);
if (unlikely (in_error ())) return;
- copy_vector (o);
+ copy_array (o.as_array ());
+ }
+ hb_vector_t (array_t o) : hb_vector_t ()
+ {
+ alloc (o.length, true);
+ if (unlikely (in_error ())) return;
+ copy_array (o);
+ }
+ hb_vector_t (c_array_t o) : hb_vector_t ()
+ {
+ alloc (o.length, true);
+ if (unlikely (in_error ())) return;
+ copy_array (o);
}
hb_vector_t (hb_vector_t &&o)
{
@@ -74,7 +86,7 @@
~hb_vector_t () { fini (); }
public:
- int allocated = 0; /* == -1 means allocation failed. */
+ int allocated = 0; /* < 0 means allocation failed. */
unsigned int length = 0;
public:
Type *arrayZ = nullptr;
@@ -90,19 +102,21 @@
void fini ()
{
- shrink_vector (0);
- hb_free (arrayZ);
+ /* We allow a hack to make the vector point to a foriegn array
+ * by the user. In that case length/arrayZ are non-zero but
+ * allocated is zero. Don't free anything. */
+ if (allocated)
+ {
+ shrink_vector (0);
+ hb_free (arrayZ);
+ }
init ();
}
void reset ()
{
if (unlikely (in_error ()))
- /* Big Hack! We don't know the true allocated size before
- * an allocation failure happened. But we know it was at
- * least as big as length. Restore it to that and continue
- * as if error did not happen. */
- allocated = length;
+ reset_error ();
resize (0);
}
@@ -119,7 +133,7 @@
alloc (o.length, true);
if (unlikely (in_error ())) return *this;
- copy_vector (o);
+ copy_array (o.as_array ());
return *this;
}
@@ -191,7 +205,7 @@
Type *push ()
{
if (unlikely (!resize (length + 1)))
- return &Crap (Type);
+ return std::addressof (Crap (Type));
return std::addressof (arrayZ[length - 1]);
}
template <typename T,
@@ -201,7 +215,7 @@
Type *push (T&& v)
{
Type *p = push ();
- if (p == &Crap (Type))
+ if (p == std::addressof (Crap (Type)))
// If push failed to allocate then don't copy v, since this may cause
// the created copy to leak memory since we won't have stored a
// reference to it.
@@ -214,24 +228,33 @@
hb_enable_if (std::is_copy_constructible<T2>::value)>
Type *push (T&& v)
{
- if (unlikely (!alloc (length + 1)))
+ if (unlikely ((int) length >= allocated && !alloc (length + 1)))
// If push failed to allocate then don't copy v, since this may cause
// the created copy to leak memory since we won't have stored a
// reference to it.
- return &Crap (Type);
+ return std::addressof (Crap (Type));
/* Emplace. */
- length++;
- Type *p = std::addressof (arrayZ[length - 1]);
+ Type *p = std::addressof (arrayZ[length++]);
return new (p) Type (std::forward<T> (v));
}
bool in_error () const { return allocated < 0; }
+ void set_error ()
+ {
+ assert (allocated >= 0);
+ allocated = -allocated - 1;
+ }
+ void reset_error ()
+ {
+ assert (allocated < 0);
+ allocated = -(allocated + 1);
+ }
template <typename T = Type,
hb_enable_if (hb_is_trivially_copy_assignable(T))>
Type *
- realloc_vector (unsigned new_allocated)
+ realloc_vector (unsigned new_allocated, hb_priority<0>)
{
if (!new_allocated)
{
@@ -243,7 +266,7 @@
template <typename T = Type,
hb_enable_if (!hb_is_trivially_copy_assignable(T))>
Type *
- realloc_vector (unsigned new_allocated)
+ realloc_vector (unsigned new_allocated, hb_priority<0>)
{
if (!new_allocated)
{
@@ -263,31 +286,52 @@
}
return new_array;
}
+ /* Specialization for hb_vector_t<hb_{vector,array}_t<U>> to speed up. */
+ template <typename T = Type,
+ hb_enable_if (hb_is_same (T, hb_vector_t<typename T::item_t>) ||
+ hb_is_same (T, hb_array_t <typename T::item_t>))>
+ Type *
+ realloc_vector (unsigned new_allocated, hb_priority<1>)
+ {
+ if (!new_allocated)
+ {
+ hb_free (arrayZ);
+ return nullptr;
+ }
+ return (Type *) hb_realloc (arrayZ, new_allocated * sizeof (Type));
+ }
template <typename T = Type,
hb_enable_if (hb_is_trivially_constructible(T))>
void
- grow_vector (unsigned size)
+ grow_vector (unsigned size, hb_priority<0>)
{
- memset (arrayZ + length, 0, (size - length) * sizeof (*arrayZ));
+ hb_memset (arrayZ + length, 0, (size - length) * sizeof (*arrayZ));
length = size;
}
template <typename T = Type,
hb_enable_if (!hb_is_trivially_constructible(T))>
void
- grow_vector (unsigned size)
+ grow_vector (unsigned size, hb_priority<0>)
{
- while (length < size)
- {
- length++;
- new (std::addressof (arrayZ[length - 1])) Type ();
- }
+ for (; length < size; length++)
+ new (std::addressof (arrayZ[length])) Type ();
+ }
+ /* Specialization for hb_vector_t<hb_{vector,array}_t<U>> to speed up. */
+ template <typename T = Type,
+ hb_enable_if (hb_is_same (T, hb_vector_t<typename T::item_t>) ||
+ hb_is_same (T, hb_array_t <typename T::item_t>))>
+ void
+ grow_vector (unsigned size, hb_priority<1>)
+ {
+ hb_memset (arrayZ + length, 0, (size - length) * sizeof (*arrayZ));
+ length = size;
}
template <typename T = Type,
hb_enable_if (hb_is_trivially_copyable (T))>
void
- copy_vector (const hb_vector_t &other)
+ copy_array (hb_array_t<const Type> other)
{
length = other.length;
if (!HB_OPTIMIZE_SIZE_VAL && sizeof (T) >= sizeof (long long))
@@ -301,7 +345,7 @@
hb_enable_if (!hb_is_trivially_copyable (T) &&
std::is_copy_constructible<T>::value)>
void
- copy_vector (const hb_vector_t &other)
+ copy_array (hb_array_t<const Type> other)
{
length = 0;
while (length < other.length)
@@ -316,7 +360,7 @@
std::is_default_constructible<T>::value &&
std::is_copy_assignable<T>::value)>
void
- copy_vector (const hb_vector_t &other)
+ copy_array (hb_array_t<const Type> other)
{
length = 0;
while (length < other.length)
@@ -330,11 +374,15 @@
void
shrink_vector (unsigned size)
{
- while ((unsigned) length > size)
+ assert (size <= length);
+ if (!std::is_trivially_destructible<Type>::value)
{
- arrayZ[(unsigned) length - 1].~Type ();
- length--;
+ unsigned count = length - size;
+ Type *p = arrayZ + length - 1;
+ while (count--)
+ p--->~Type ();
}
+ length = size;
}
void
@@ -381,18 +429,18 @@
if (unlikely (overflows))
{
- allocated = -1;
+ set_error ();
return false;
}
- Type *new_array = realloc_vector (new_allocated);
+ Type *new_array = realloc_vector (new_allocated, hb_prioritize);
if (unlikely (new_allocated && !new_array))
{
if (new_allocated <= (unsigned) allocated)
return true; // shrinking failed; it's okay; happens in our fuzzer
- allocated = -1;
+ set_error ();
return false;
}
@@ -411,7 +459,7 @@
if (size > length)
{
if (initialize)
- grow_vector (size);
+ grow_vector (size, hb_prioritize);
}
else if (size < length)
{
diff --git a/src/hb-version.h b/src/hb-version.h
index 08d1f55..c290122 100644
--- a/src/hb-version.h
+++ b/src/hb-version.h
@@ -41,26 +41,26 @@
*
* The major component of the library version available at compile-time.
*/
-#define HB_VERSION_MAJOR 7
+#define HB_VERSION_MAJOR 8
/**
* HB_VERSION_MINOR:
*
* The minor component of the library version available at compile-time.
*/
-#define HB_VERSION_MINOR 3
+#define HB_VERSION_MINOR 0
/**
* HB_VERSION_MICRO:
*
* The micro component of the library version available at compile-time.
*/
-#define HB_VERSION_MICRO 0
+#define HB_VERSION_MICRO 1
/**
* HB_VERSION_STRING:
*
* A string literal containing the library version available at compile-time.
*/
-#define HB_VERSION_STRING "7.3.0"
+#define HB_VERSION_STRING "8.0.1"
/**
* HB_VERSION_ATLEAST:
diff --git a/src/hb-subset-cff1.hh b/src/hb-wasm-api-blob.hh
similarity index 71%
rename from src/hb-subset-cff1.hh
rename to src/hb-wasm-api-blob.hh
index aaf5def..310f402 100644
--- a/src/hb-subset-cff1.hh
+++ b/src/hb-wasm-api-blob.hh
@@ -1,5 +1,5 @@
/*
- * Copyright © 2018 Adobe Inc.
+ * Copyright © 2023 Behdad Esfahbod
*
* This is part of HarfBuzz, a text shaping library.
*
@@ -20,18 +20,31 @@
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Adobe Author(s): Michiharu Ariza
*/
-#ifndef HB_SUBSET_CFF1_HH
-#define HB_SUBSET_CFF1_HH
+#ifndef HB_WASM_API_BLOB_HH
+#define HB_WASM_API_BLOB_HH
-#include "hb.hh"
+#include "hb-wasm-api.hh"
-#include "hb-subset-plan.hh"
+namespace hb {
+namespace wasm {
-HB_INTERNAL bool
-hb_subset_cff1 (hb_subset_context_t *c);
-#endif /* HB_SUBSET_CFF1_HH */
+HB_WASM_API (void, blob_free) (HB_WASM_EXEC_ENV
+ ptr_d(blob_t, blob))
+{
+ HB_PTR_PARAM (blob_t, blob);
+ if (unlikely (!blob))
+ return;
+
+ module_free (blob->data);
+
+ blob->data = nullref;
+ blob->length = 0;
+}
+
+
+}}
+
+#endif /* HB_WASM_API_BLOB_HH */
diff --git a/src/hb-wasm-api-buffer.hh b/src/hb-wasm-api-buffer.hh
new file mode 100644
index 0000000..64217a0
--- /dev/null
+++ b/src/hb-wasm-api-buffer.hh
@@ -0,0 +1,217 @@
+/*
+ * Copyright © 2023 Behdad Esfahbod
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef HB_WASM_API_BUFFER_HH
+#define HB_WASM_API_BUFFER_HH
+
+#include "hb-wasm-api.hh"
+
+#include "hb-buffer.hh"
+
+namespace hb {
+namespace wasm {
+
+static_assert (sizeof (glyph_info_t) == sizeof (hb_glyph_info_t), "");
+static_assert (sizeof (glyph_position_t) == sizeof (hb_glyph_position_t), "");
+
+HB_WASM_API (bool_t, buffer_contents_realloc) (HB_WASM_EXEC_ENV
+ ptr_d(buffer_contents_t, contents),
+ uint32_t size)
+{
+ HB_PTR_PARAM (buffer_contents_t, contents);
+ if (unlikely (!contents))
+ return false;
+
+ if (size <= contents->length)
+ return true;
+
+ unsigned bytes;
+ if (hb_unsigned_mul_overflows (size, sizeof (glyph_info_t), &bytes))
+ return false;
+
+ glyph_info_t *info = HB_ARRAY_APP2NATIVE (glyph_info_t, contents->info, contents->length);
+ glyph_position_t *pos = HB_ARRAY_APP2NATIVE (glyph_position_t, contents->pos, contents->length);
+
+ if (unlikely (!info || !pos))
+ return false;
+
+ glyph_info_t *new_info = nullptr;
+ uint32_t new_inforef = module_malloc (bytes, (void **) &new_info);
+ glyph_position_t *new_pos = nullptr;
+ uint32_t new_posref = module_malloc (bytes, (void **) &new_pos);
+
+ unsigned old_bytes = contents->length * sizeof (glyph_info_t);
+ if (likely (new_inforef))
+ {
+ hb_memcpy (new_info, info, old_bytes);
+ module_free (contents->info);
+ contents->info = new_inforef;
+ }
+ if (likely (new_posref))
+ {
+ hb_memcpy (new_pos, pos, old_bytes);
+ module_free (contents->pos);
+ contents->pos = new_posref;
+ }
+
+ if (likely (new_info && new_pos))
+ {
+ contents->length = size;
+ return true;
+ }
+
+ return false;
+}
+
+HB_WASM_API (void, buffer_contents_free) (HB_WASM_EXEC_ENV
+ ptr_d(buffer_contents_t, contents))
+{
+ HB_PTR_PARAM (buffer_contents_t, contents);
+ if (unlikely (!contents))
+ return;
+
+ module_free (contents->info);
+ module_free (contents->pos);
+
+ contents->info = nullref;
+ contents->pos = nullref;
+ contents->length = 0;
+}
+
+HB_WASM_API (bool_t, buffer_copy_contents) (HB_WASM_EXEC_ENV
+ ptr_d(buffer_t, buffer),
+ ptr_d(buffer_contents_t, contents))
+{
+ HB_REF2OBJ (buffer);
+ HB_PTR_PARAM (buffer_contents_t, contents);
+ if (unlikely (!contents))
+ return false;
+
+ if (buffer->have_output)
+ buffer->sync ();
+ if (!buffer->have_positions)
+ buffer->clear_positions ();
+
+ unsigned length = buffer->len;
+
+ if (length <= contents->length)
+ {
+ glyph_info_t *info = HB_ARRAY_APP2NATIVE (glyph_info_t, contents->info, length);
+ glyph_position_t *pos = HB_ARRAY_APP2NATIVE (glyph_position_t, contents->pos, length);
+
+ if (unlikely (!info || !pos))
+ {
+ contents->length = 0;
+ return false;
+ }
+
+ unsigned bytes = length * sizeof (hb_glyph_info_t);
+ hb_memcpy (info, buffer->info, bytes);
+ hb_memcpy (pos, buffer->pos, bytes);
+
+ return true;
+ }
+
+ module_free (contents->info);
+ module_free (contents->pos);
+
+ contents->length = length;
+ unsigned bytes = length * sizeof (hb_glyph_info_t);
+ contents->info = wasm_runtime_module_dup_data (module_inst, (const char *) buffer->info, bytes);
+ contents->pos = wasm_runtime_module_dup_data (module_inst, (const char *) buffer->pos, bytes);
+
+ if (length && (!contents->info || !contents->pos))
+ {
+ contents->length = 0;
+ return false;
+ }
+
+ return true;
+}
+
+HB_WASM_API (bool_t, buffer_set_contents) (HB_WASM_EXEC_ENV
+ ptr_d(buffer_t, buffer),
+ ptr_d(const buffer_contents_t, contents))
+{
+ HB_REF2OBJ (buffer);
+ HB_PTR_PARAM (buffer_contents_t, contents);
+ if (unlikely (!contents))
+ return false;
+
+ unsigned length = contents->length;
+ unsigned bytes;
+ if (unlikely (hb_unsigned_mul_overflows (length, sizeof (buffer->info[0]), &bytes)))
+ return false;
+
+ if (unlikely (!buffer->resize (length)))
+ return false;
+
+ glyph_info_t *info = (glyph_info_t *) (validate_app_addr (contents->info, bytes) ? addr_app_to_native (contents->info) : nullptr);
+ glyph_position_t *pos = (glyph_position_t *) (validate_app_addr (contents->pos, bytes) ? addr_app_to_native (contents->pos) : nullptr);
+
+ if (!buffer->have_positions)
+ buffer->clear_positions (); /* This is wasteful. */
+
+ hb_memcpy (buffer->info, info, bytes);
+ hb_memcpy (buffer->pos, pos, bytes);
+ buffer->len = length;
+
+ return true;
+}
+
+HB_WASM_API (direction_t, buffer_get_direction) (HB_WASM_EXEC_ENV
+ ptr_d(buffer_t, buffer))
+{
+ HB_REF2OBJ (buffer);
+
+ return (direction_t) hb_buffer_get_direction (buffer);
+}
+
+HB_WASM_API (script_t, buffer_get_script) (HB_WASM_EXEC_ENV
+ ptr_d(buffer_t, buffer))
+{
+ HB_REF2OBJ (buffer);
+
+ return hb_script_to_iso15924_tag (hb_buffer_get_script (buffer));
+}
+
+HB_WASM_API (void, buffer_reverse) (HB_WASM_EXEC_ENV
+ ptr_d(buffer_t, buffer))
+{
+ HB_REF2OBJ (buffer);
+
+ hb_buffer_reverse (buffer);
+}
+
+HB_WASM_API (void, buffer_reverse_clusters) (HB_WASM_EXEC_ENV
+ ptr_d(buffer_t, buffer))
+{
+ HB_REF2OBJ (buffer);
+
+ hb_buffer_reverse_clusters (buffer);
+}
+
+}}
+
+#endif /* HB_WASM_API_BUFFER_HH */
diff --git a/src/hb-subset-cff1.hh b/src/hb-wasm-api-common.hh
similarity index 71%
copy from src/hb-subset-cff1.hh
copy to src/hb-wasm-api-common.hh
index aaf5def..c38ca9c 100644
--- a/src/hb-subset-cff1.hh
+++ b/src/hb-wasm-api-common.hh
@@ -1,5 +1,5 @@
/*
- * Copyright © 2018 Adobe Inc.
+ * Copyright © 2023 Behdad Esfahbod
*
* This is part of HarfBuzz, a text shaping library.
*
@@ -20,18 +20,25 @@
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Adobe Author(s): Michiharu Ariza
*/
-#ifndef HB_SUBSET_CFF1_HH
-#define HB_SUBSET_CFF1_HH
+#ifndef HB_WASM_API_COMMON_HH
+#define HB_WASM_API_COMMON_HH
-#include "hb.hh"
+#include "hb-wasm-api.hh"
-#include "hb-subset-plan.hh"
+namespace hb {
+namespace wasm {
-HB_INTERNAL bool
-hb_subset_cff1 (hb_subset_context_t *c);
-#endif /* HB_SUBSET_CFF1_HH */
+HB_WASM_API (direction_t, script_get_horizontal_direction) (HB_WASM_EXEC_ENV
+ script_t script)
+{
+ return (direction_t)
+ hb_script_get_horizontal_direction (hb_script_from_iso15924_tag (script));
+}
+
+
+}}
+
+#endif /* HB_WASM_API_COMMON_HH */
diff --git a/src/hb-wasm-api-face.hh b/src/hb-wasm-api-face.hh
new file mode 100644
index 0000000..22e50e9
--- /dev/null
+++ b/src/hb-wasm-api-face.hh
@@ -0,0 +1,109 @@
+/*
+ * Copyright © 2023 Behdad Esfahbod
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef HB_WASM_API_FACE_HH
+#define HB_WASM_API_FACE_HH
+
+#include "hb-wasm-api.hh"
+
+namespace hb {
+namespace wasm {
+
+
+HB_WASM_API (ptr_t(face_t), face_create) (HB_WASM_EXEC_ENV
+ ptr_d(blob_t, blob),
+ unsigned int index)
+{
+ HB_PTR_PARAM (blob_t, blob);
+ hb_blob_t *hb_blob = hb_blob_create(
+ HB_ARRAY_APP2NATIVE (char, blob->data, blob->length),
+ blob->length,
+ HB_MEMORY_MODE_DUPLICATE,
+ NULL,
+ NULL);
+
+ hb_face_t *face = hb_face_create(hb_blob, index);
+
+ HB_OBJ2REF (face);
+ return faceref;
+}
+
+HB_WASM_API (bool_t, face_copy_table) (HB_WASM_EXEC_ENV
+ ptr_d(face_t, face),
+ tag_t table_tag,
+ ptr_d(blob_t, blob))
+{
+ HB_REF2OBJ (face);
+ HB_PTR_PARAM (blob_t, blob);
+ if (unlikely (!blob))
+ return false;
+
+ hb_blob_t *hb_blob = hb_face_reference_table (face, table_tag);
+
+ unsigned length;
+ const char *hb_data = hb_blob_get_data (hb_blob, &length);
+
+ if (length <= blob->length)
+ {
+ char *data = HB_ARRAY_APP2NATIVE (char, blob->data, length);
+
+ if (unlikely (!data))
+ {
+ blob->length = 0;
+ return false;
+ }
+
+ hb_memcpy (data, hb_data, length);
+
+ return true;
+ }
+
+ module_free (blob->data);
+
+ blob->length = length;
+ blob->data = wasm_runtime_module_dup_data (module_inst, hb_data, length);
+
+ hb_blob_destroy (hb_blob);
+
+ if (blob->length && !blob->data)
+ {
+ blob->length = 0;
+ return false;
+ }
+
+ return true;
+}
+
+HB_WASM_API (unsigned, face_get_upem) (HB_WASM_EXEC_ENV
+ ptr_d(face_t, face))
+{
+ HB_REF2OBJ (face);
+
+ return hb_face_get_upem (face);
+}
+
+
+}}
+
+#endif /* HB_WASM_API_FACE_HH */
diff --git a/src/hb-wasm-api-font.hh b/src/hb-wasm-api-font.hh
new file mode 100644
index 0000000..2d85db4
--- /dev/null
+++ b/src/hb-wasm-api-font.hh
@@ -0,0 +1,263 @@
+/*
+ * Copyright © 2023 Behdad Esfahbod
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef HB_WASM_API_FONT_HH
+#define HB_WASM_API_FONT_HH
+
+#include "hb-wasm-api.hh"
+
+#include "hb-outline.hh"
+
+namespace hb {
+namespace wasm {
+
+
+HB_WASM_API (ptr_t(font_t), font_create) (HB_WASM_EXEC_ENV
+ ptr_d(face_t, face))
+{
+ HB_REF2OBJ (face);
+
+ hb_font_t *font = hb_font_create (face);
+
+ HB_OBJ2REF (font);
+ return fontref;
+}
+
+HB_WASM_API (ptr_t(face_t), font_get_face) (HB_WASM_EXEC_ENV
+ ptr_d(font_t, font))
+{
+ HB_REF2OBJ (font);
+
+ hb_face_t *face = hb_font_get_face (font);
+
+ HB_OBJ2REF (face);
+ return faceref;
+}
+
+HB_WASM_API (void, font_get_scale) (HB_WASM_EXEC_ENV
+ ptr_d(font_t, font),
+ ptr_d(int32_t, x_scale),
+ ptr_d(int32_t, y_scale))
+{
+ HB_REF2OBJ (font);
+
+ HB_PTR_PARAM(int32_t, x_scale);
+ HB_PTR_PARAM(int32_t, y_scale);
+
+ hb_font_get_scale (font, x_scale, y_scale);
+}
+
+HB_WASM_API (codepoint_t, font_get_glyph) (HB_WASM_EXEC_ENV
+ ptr_d(font_t, font),
+ codepoint_t unicode,
+ codepoint_t variation_selector)
+{
+ HB_REF2OBJ (font);
+ codepoint_t glyph;
+
+ hb_font_get_glyph (font, unicode, variation_selector, &glyph);
+ return glyph;
+}
+
+HB_WASM_API (position_t, font_get_glyph_h_advance) (HB_WASM_EXEC_ENV
+ ptr_d(font_t, font),
+ codepoint_t glyph)
+{
+ HB_REF2OBJ (font);
+ return hb_font_get_glyph_h_advance (font, glyph);
+}
+
+HB_WASM_API (position_t, font_get_glyph_v_advance) (HB_WASM_EXEC_ENV
+ ptr_d(font_t, font),
+ codepoint_t glyph)
+{
+ HB_REF2OBJ (font);
+ return hb_font_get_glyph_v_advance (font, glyph);
+}
+
+static_assert (sizeof (glyph_extents_t) == sizeof (hb_glyph_extents_t), "");
+
+HB_WASM_API (bool_t, font_get_glyph_extents) (HB_WASM_EXEC_ENV
+ ptr_d(font_t, font),
+ codepoint_t glyph,
+ ptr_d(glyph_extents_t, extents))
+{
+ HB_REF2OBJ (font);
+ HB_PTR_PARAM (glyph_extents_t, extents);
+ if (unlikely (!extents))
+ return false;
+
+ return hb_font_get_glyph_extents (font, glyph,
+ (hb_glyph_extents_t *) extents);
+}
+
+HB_WASM_API (void, font_glyph_to_string) (HB_WASM_EXEC_ENV
+ ptr_d(font_t, font),
+ codepoint_t glyph,
+ char *s, uint32_t size)
+{
+ HB_REF2OBJ (font);
+
+ hb_font_glyph_to_string (font, glyph, s, size);
+}
+
+static_assert (sizeof (glyph_outline_point_t) == sizeof (hb_outline_point_t), "");
+static_assert (sizeof (uint32_t) == sizeof (hb_outline_t::contours[0]), "");
+
+HB_WASM_API (bool_t, font_copy_glyph_outline) (HB_WASM_EXEC_ENV
+ ptr_d(font_t, font),
+ codepoint_t glyph,
+ ptr_d(glyph_outline_t, outline))
+{
+ HB_REF2OBJ (font);
+ HB_PTR_PARAM (glyph_outline_t, outline);
+ if (unlikely (!outline))
+ return false;
+
+ hb_outline_t hb_outline;
+ auto *funcs = hb_outline_recording_pen_get_funcs ();
+
+ hb_font_draw_glyph (font, glyph, funcs, &hb_outline);
+
+ if (unlikely (hb_outline.points.in_error () ||
+ hb_outline.contours.in_error ()))
+ {
+ outline->n_points = outline->n_contours = 0;
+ return false;
+ }
+
+ // TODO Check two buffers separately
+ if (hb_outline.points.length <= outline->n_points &&
+ hb_outline.contours.length <= outline->n_contours)
+ {
+ glyph_outline_point_t *points = HB_ARRAY_APP2NATIVE (glyph_outline_point_t, outline->points, hb_outline.points.length);
+ uint32_t *contours = HB_ARRAY_APP2NATIVE (uint32_t, outline->contours, hb_outline.contours.length);
+
+ if (unlikely (!points || !contours))
+ {
+ outline->n_points = outline->n_contours = 0;
+ return false;
+ }
+
+ hb_memcpy (points, hb_outline.points.arrayZ, hb_outline.points.get_size ());
+ hb_memcpy (contours, hb_outline.contours.arrayZ, hb_outline.contours.get_size ());
+
+ return true;
+ }
+
+ outline->n_points = hb_outline.points.length;
+ outline->points = wasm_runtime_module_dup_data (module_inst,
+ (const char *) hb_outline.points.arrayZ,
+ hb_outline.points.get_size ());
+ outline->n_contours = hb_outline.contours.length;
+ outline->contours = wasm_runtime_module_dup_data (module_inst,
+ (const char *) hb_outline.contours.arrayZ,
+ hb_outline.contours.get_size ());
+
+ if ((outline->n_points && !outline->points) ||
+ (!outline->n_contours && !outline->contours))
+ {
+ outline->n_points = outline->n_contours = 0;
+ return false;
+ }
+
+ return true;
+}
+
+HB_WASM_API (void, glyph_outline_free) (HB_WASM_EXEC_ENV
+ ptr_d(glyph_outline_t, outline))
+{
+ HB_PTR_PARAM (glyph_outline_t, outline);
+ if (unlikely (!outline))
+ return;
+
+ module_free (outline->points);
+ module_free (outline->contours);
+
+ outline->n_points = 0;
+ outline->points = nullref;
+ outline->n_contours = 0;
+ outline->contours = nullref;
+}
+
+HB_WASM_API (bool_t, font_copy_coords) (HB_WASM_EXEC_ENV
+ ptr_d(font_t, font),
+ ptr_d(coords_t, coords))
+{
+ HB_REF2OBJ (font);
+ HB_PTR_PARAM (coords_t, coords);
+ if (unlikely (!coords))
+ return false;
+
+ unsigned our_length;
+ const int* our_coords = hb_font_get_var_coords_normalized(font, &our_length);
+
+ if (our_length <= coords->length) {
+ int *their_coords = HB_ARRAY_APP2NATIVE (int, coords->coords, our_length);
+ if (unlikely(!their_coords)) {
+ coords->length = 0;
+ return false;
+ }
+ unsigned bytes = our_length * sizeof (int);
+ hb_memcpy (their_coords, our_coords, bytes);
+
+ return true;
+ }
+
+ module_free (coords->coords);
+ coords->length = our_length;
+ unsigned bytes = our_length * sizeof (int);
+ coords->coords = wasm_runtime_module_dup_data (module_inst, (const char *) our_coords, bytes);
+ if (our_length && !coords->coords)
+ {
+ coords->length = 0;
+ return false;
+ }
+
+ return true;
+}
+
+HB_WASM_API (bool_t, font_set_coords) (HB_WASM_EXEC_ENV
+ ptr_d(font_t, font),
+ ptr_d(coords_t, coords))
+{
+ HB_REF2OBJ (font);
+ HB_PTR_PARAM (coords_t, coords);
+ if (unlikely (!coords))
+ return false;
+
+ unsigned length = coords->length;
+ unsigned bytes;
+ if (unlikely (hb_unsigned_mul_overflows (length, sizeof (int), &bytes)))
+ return false;
+
+ const int *our_coords = (const int *) (validate_app_addr (coords->coords, bytes) ? addr_app_to_native (coords->coords) : nullptr);
+ hb_font_set_var_coords_normalized(font, our_coords, length);
+ return true;
+}
+
+
+}}
+
+#endif /* HB_WASM_API_FONT_HH */
diff --git a/src/hb-wasm-api-list.hh b/src/hb-wasm-api-list.hh
new file mode 100644
index 0000000..5db45c7
--- /dev/null
+++ b/src/hb-wasm-api-list.hh
@@ -0,0 +1,109 @@
+/*
+ * Copyright © 2023 Behdad Esfahbod
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef HB_WASM_API_LIST_HH
+#define HB_WASM_API_LIST_HH
+
+#include "hb-wasm-api.hh"
+
+
+#ifdef HB_DEBUG_WASM
+namespace hb { namespace wasm {
+
+static void debugprint (HB_WASM_EXEC_ENV char *str)
+{ DEBUG_MSG (WASM, exec_env, "%s", str); }
+static void debugprint1 (HB_WASM_EXEC_ENV char *str, int32_t i1)
+{ DEBUG_MSG (WASM, exec_env, "%s: %d", str, i1); }
+static void debugprint2 (HB_WASM_EXEC_ENV char *str, int32_t i1, int32_t i2)
+{ DEBUG_MSG (WASM, exec_env, "%s: %d, %d", str, i1, i2); }
+static void debugprint3 (HB_WASM_EXEC_ENV char *str, int32_t i1, int32_t i2, int32_t i3)
+{ DEBUG_MSG (WASM, exec_env, "%s: %d, %d, %d", str, i1, i2, i3); }
+static void debugprint4 (HB_WASM_EXEC_ENV char *str, int32_t i1, int32_t i2, int32_t i3, int32_t i4)
+{ DEBUG_MSG (WASM, exec_env, "%s: %d, %d, %d, %d", str, i1, i2, i3, i4); }
+
+}}
+#endif
+
+#define NATIVE_SYMBOL(signature, name) {#name, (void *) hb::wasm::name, signature, NULL}
+/* Note: the array must be static defined since runtime will keep it after registration.
+ * Also not const, because it modifies it (sorts it).
+ * https://github.com/bytecodealliance/wasm-micro-runtime/blob/main/doc/export_native_api.md
+ *
+ * TODO Allocate this lazily in _hb_wasm_init(). */
+static NativeSymbol _hb_wasm_native_symbols[] =
+{
+ /* common */
+ NATIVE_SYMBOL ("(i)i", script_get_horizontal_direction),
+
+ /* blob */
+ NATIVE_SYMBOL ("(i)", blob_free),
+
+ /* buffer */
+ NATIVE_SYMBOL ("(i)", buffer_contents_free),
+ NATIVE_SYMBOL ("(ii)i", buffer_contents_realloc),
+ NATIVE_SYMBOL ("(ii)i", buffer_copy_contents),
+ NATIVE_SYMBOL ("(ii)i", buffer_set_contents),
+ NATIVE_SYMBOL ("(i)i", buffer_get_direction),
+ NATIVE_SYMBOL ("(i)i", buffer_get_script),
+ NATIVE_SYMBOL ("(i)", buffer_reverse),
+ NATIVE_SYMBOL ("(i)", buffer_reverse_clusters),
+
+ /* face */
+ NATIVE_SYMBOL ("(ii)i", face_create),
+ NATIVE_SYMBOL ("(iii)i", face_copy_table),
+ NATIVE_SYMBOL ("(i)i", face_get_upem),
+
+ /* font */
+ NATIVE_SYMBOL ("(i)i", font_create),
+ NATIVE_SYMBOL ("(i)i", font_get_face),
+ NATIVE_SYMBOL ("(iii)", font_get_scale),
+ NATIVE_SYMBOL ("(iii)i", font_get_glyph),
+ NATIVE_SYMBOL ("(ii)i", font_get_glyph_h_advance),
+ NATIVE_SYMBOL ("(ii)i", font_get_glyph_v_advance),
+ NATIVE_SYMBOL ("(iii)i", font_get_glyph_extents),
+ NATIVE_SYMBOL ("(ii$*)", font_glyph_to_string),
+ NATIVE_SYMBOL ("(iii)i", font_copy_glyph_outline),
+ NATIVE_SYMBOL ("(ii)i", font_copy_coords),
+ NATIVE_SYMBOL ("(ii)i", font_set_coords),
+
+ /* outline */
+ NATIVE_SYMBOL ("(i)", glyph_outline_free),
+
+ /* shape */
+ NATIVE_SYMBOL ("(iiii$)i", shape_with),
+
+ /* debug */
+#ifdef HB_DEBUG_WASM
+ NATIVE_SYMBOL ("($)", debugprint),
+ NATIVE_SYMBOL ("($i)", debugprint1),
+ NATIVE_SYMBOL ("($ii)", debugprint2),
+ NATIVE_SYMBOL ("($iii)", debugprint3),
+ NATIVE_SYMBOL ("($iiii)", debugprint4),
+#endif
+
+};
+#undef NATIVE_SYMBOL
+
+
+#endif /* HB_WASM_API_LIST_HH */
diff --git a/src/hb-wasm-api-shape.hh b/src/hb-wasm-api-shape.hh
new file mode 100644
index 0000000..622ff05
--- /dev/null
+++ b/src/hb-wasm-api-shape.hh
@@ -0,0 +1,70 @@
+/*
+ * Copyright © 2023 Behdad Esfahbod
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef HB_WASM_API_SHAPE_HH
+#define HB_WASM_API_SHAPE_HH
+
+#include "hb-wasm-api.hh"
+
+namespace hb {
+namespace wasm {
+
+
+static_assert (sizeof (feature_t) == sizeof (hb_feature_t), "");
+
+HB_WASM_INTERFACE (bool_t, shape_with) (HB_WASM_EXEC_ENV
+ ptr_d(font_t, font),
+ ptr_d(buffer_t, buffer),
+ ptr_d(const feature_t, features),
+ uint32_t num_features,
+ const char *shaper)
+{
+ if (unlikely (0 == strcmp (shaper, "wasm")))
+ return false;
+
+ HB_REF2OBJ (font);
+ HB_REF2OBJ (buffer);
+
+ /* Pre-conditions that make hb_shape_full() crash should be checked here. */
+
+ if (unlikely (!buffer->ensure_unicode ()))
+ return false;
+
+ if (unlikely (!HB_DIRECTION_IS_VALID (buffer->props.direction)))
+ return false;
+
+ HB_ARRAY_PARAM (const feature_t, features, num_features);
+ if (unlikely (!features && num_features))
+ return false;
+
+ const char * shaper_list[] = {shaper, nullptr};
+ return hb_shape_full (font, buffer,
+ (hb_feature_t *) features, num_features,
+ shaper_list);
+}
+
+
+}}
+
+#endif /* HB_WASM_API_SHAPE_HH */
diff --git a/src/hb-subset-cff1.hh b/src/hb-wasm-api.cc
similarity index 71%
copy from src/hb-subset-cff1.hh
copy to src/hb-wasm-api.cc
index aaf5def..1da8347 100644
--- a/src/hb-subset-cff1.hh
+++ b/src/hb-wasm-api.cc
@@ -1,5 +1,5 @@
/*
- * Copyright © 2018 Adobe Inc.
+ * Copyright © 2023 Behdad Esfahbod
*
* This is part of HarfBuzz, a text shaping library.
*
@@ -20,18 +20,27 @@
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Adobe Author(s): Michiharu Ariza
*/
-#ifndef HB_SUBSET_CFF1_HH
-#define HB_SUBSET_CFF1_HH
-
#include "hb.hh"
-#include "hb-subset-plan.hh"
+#ifdef HAVE_WASM
-HB_INTERNAL bool
-hb_subset_cff1 (hb_subset_context_t *c);
+#include "hb-wasm-api.hh"
-#endif /* HB_SUBSET_CFF1_HH */
+#define module_inst wasm_runtime_get_module_inst (exec_env)
+
+
+#include "hb-wasm-api-blob.hh"
+#include "hb-wasm-api-buffer.hh"
+#include "hb-wasm-api-common.hh"
+#include "hb-wasm-api-face.hh"
+#include "hb-wasm-api-font.hh"
+#include "hb-wasm-api-shape.hh"
+
+
+#undef module_inst
+
+hb_user_data_key_t _hb_wasm_ref_type_key = {};
+
+#endif
diff --git a/src/hb-wasm-api.h b/src/hb-wasm-api.h
new file mode 100644
index 0000000..f9bd98e
--- /dev/null
+++ b/src/hb-wasm-api.h
@@ -0,0 +1,319 @@
+/*
+ * Copyright © 2023 Behdad Esfahbod
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef HB_WASM_API_H
+#define HB_WASM_API_H
+
+/*
+#include "hb.h"
+*/
+
+#include <stdint.h>
+
+
+#ifndef HB_WASM_BEGIN_DECLS
+# ifdef __cplusplus
+# define HB_WASM_BEGIN_DECLS extern "C" {
+# define HB_WASM_END_DECLS }
+# else /* !__cplusplus */
+# define HB_WASM_BEGIN_DECLS
+# define HB_WASM_END_DECLS
+# endif /* !__cplusplus */
+#endif
+
+
+HB_WASM_BEGIN_DECLS
+
+#ifndef HB_WASM_API
+#define HB_WASM_API(ret_t, name) ret_t name
+#endif
+#ifndef HB_WASM_API_COMPOUND /* compound return type */
+#define HB_WASM_API_COMPOUND(ret_t, name) HB_WASM_API(ret_t, name)
+#endif
+#ifndef HB_WASM_INTERFACE
+#define HB_WASM_INTERFACE(ret_t, name) ret_t name
+#endif
+#ifndef HB_WASM_EXEC_ENV
+#define HB_WASM_EXEC_ENV
+#endif
+#ifndef HB_WASM_EXEC_ENV_COMPOUND
+#define HB_WASM_EXEC_ENV_COMPOUND HB_WASM_EXEC_ENV
+#endif
+
+
+#ifndef bool_t
+#define bool_t uint32_t
+#endif
+#ifndef ptr_t
+#define ptr_t(type_t) type_t *
+#endif
+#ifndef ptr_d
+#define ptr_d(type_t, name) type_t *name
+#endif
+
+typedef uint32_t codepoint_t;
+typedef int32_t position_t;
+typedef uint32_t mask_t;
+typedef uint32_t tag_t;
+#define TAG(c1,c2,c3,c4) ((tag_t)((((uint32_t)(c1)&0xFF)<<24)|(((uint32_t)(c2)&0xFF)<<16)|(((uint32_t)(c3)&0xFF)<<8)|((uint32_t)(c4)&0xFF)))
+
+typedef enum {
+ DIRECTION_INVALID = 0,
+ DIRECTION_LTR = 4,
+ DIRECTION_RTL,
+ DIRECTION_TTB,
+ DIRECTION_BTT
+} direction_t;
+#define DIRECTION_IS_VALID(dir) ((((unsigned int) (dir)) & ~3U) == 4)
+#define DIRECTION_IS_HORIZONTAL(dir) ((((unsigned int) (dir)) & ~1U) == 4)
+#define DIRECTION_IS_VERTICAL(dir) ((((unsigned int) (dir)) & ~1U) == 6)
+#define DIRECTION_IS_FORWARD(dir) ((((unsigned int) (dir)) & ~2U) == 4)
+#define DIRECTION_IS_BACKWARD(dir) ((((unsigned int) (dir)) & ~2U) == 5)
+#define DIRECTION_REVERSE(dir) ((direction_t) (((unsigned int) (dir)) ^ 1))
+
+typedef tag_t script_t; /* ISO 15924 representation of Unicode scripts. */
+
+
+/* common */
+
+HB_WASM_API (direction_t, script_get_horizontal_direction) (HB_WASM_EXEC_ENV
+ script_t script);
+
+
+/* blob */
+
+typedef struct
+{
+ uint32_t length;
+ ptr_t(char) data;
+} blob_t;
+#define BLOB_INIT {0, 0}
+
+HB_WASM_API (void, blob_free) (HB_WASM_EXEC_ENV
+ ptr_d(blob_t, blob));
+
+/* buffer */
+
+typedef struct
+{
+ uint32_t codepoint;
+ uint32_t mask;
+ uint32_t cluster;
+ uint32_t var1;
+ uint32_t var2;
+} glyph_info_t;
+
+typedef struct
+{
+ position_t x_advance;
+ position_t y_advance;
+ position_t x_offset;
+ position_t y_offset;
+ uint32_t var;
+} glyph_position_t;
+
+typedef struct
+{
+ uint32_t length;
+ ptr_t(glyph_info_t) info;
+ ptr_t(glyph_position_t) pos;
+} buffer_contents_t;
+#define BUFFER_CONTENTS_INIT {0, 0, 0}
+
+HB_WASM_API (bool_t, buffer_contents_realloc) (HB_WASM_EXEC_ENV
+ ptr_d(buffer_contents_t, contents),
+ uint32_t size);
+
+HB_WASM_API (void, buffer_contents_free) (HB_WASM_EXEC_ENV
+ ptr_d(buffer_contents_t, contents));
+
+typedef struct buffer_t buffer_t;
+
+HB_WASM_API (bool_t, buffer_copy_contents) (HB_WASM_EXEC_ENV
+ ptr_d(buffer_t, buffer),
+ ptr_d(buffer_contents_t, contents));
+
+HB_WASM_API (bool_t, buffer_set_contents) (HB_WASM_EXEC_ENV
+ ptr_d(buffer_t, buffer),
+ ptr_d(const buffer_contents_t, contents));
+
+HB_WASM_API (direction_t, buffer_get_direction) (HB_WASM_EXEC_ENV
+ ptr_d(buffer_t, buffer));
+
+HB_WASM_API (script_t, buffer_get_script) (HB_WASM_EXEC_ENV
+ ptr_d(buffer_t, buffer));
+
+HB_WASM_API (void, buffer_reverse) (HB_WASM_EXEC_ENV
+ ptr_d(buffer_t, buffer));
+
+HB_WASM_API (void, buffer_reverse_clusters) (HB_WASM_EXEC_ENV
+ ptr_d(buffer_t, buffer));
+
+/* face */
+
+typedef struct face_t face_t;
+
+HB_WASM_API (ptr_t(face_t), face_create) (HB_WASM_EXEC_ENV
+ ptr_d(blob_t, blob),
+ unsigned int);
+
+HB_WASM_API (bool_t, face_copy_table) (HB_WASM_EXEC_ENV
+ ptr_d(face_t, face),
+ tag_t table_tag,
+ ptr_d(blob_t, blob));
+
+HB_WASM_API (unsigned, face_get_upem) (HB_WASM_EXEC_ENV
+ ptr_d(face_t, face));
+
+/* font */
+
+typedef struct font_t font_t;
+
+HB_WASM_API (ptr_t(font_t), font_create) (HB_WASM_EXEC_ENV
+ ptr_d(face_t, face));
+
+HB_WASM_API (ptr_t(face_t), font_get_face) (HB_WASM_EXEC_ENV
+ ptr_d(font_t, font));
+
+HB_WASM_API (void, font_get_scale) (HB_WASM_EXEC_ENV
+ ptr_d(font_t, font),
+ ptr_d(int32_t, x_scale),
+ ptr_d(int32_t, y_scale));
+
+HB_WASM_API (codepoint_t, font_get_glyph) (HB_WASM_EXEC_ENV
+ ptr_d(font_t, font),
+ codepoint_t unicode,
+ codepoint_t variation_selector);
+
+HB_WASM_API (position_t, font_get_glyph_h_advance) (HB_WASM_EXEC_ENV
+ ptr_d(font_t, font),
+ codepoint_t glyph);
+
+HB_WASM_API (position_t, font_get_glyph_v_advance) (HB_WASM_EXEC_ENV
+ ptr_d(font_t, font),
+ codepoint_t glyph);
+
+typedef struct
+{
+ position_t x_bearing;
+ position_t y_bearing;
+ position_t width;
+ position_t height;
+} glyph_extents_t;
+
+HB_WASM_API (bool_t, font_get_glyph_extents) (HB_WASM_EXEC_ENV
+ ptr_d(font_t, font),
+ codepoint_t glyph,
+ ptr_d(glyph_extents_t, extents));
+
+HB_WASM_API (void, font_glyph_to_string) (HB_WASM_EXEC_ENV
+ ptr_d(font_t, font),
+ codepoint_t glyph,
+ char *s, uint32_t size);
+
+
+typedef struct
+{
+ unsigned int length;
+ ptr_t(int) coords;
+} coords_t;
+
+HB_WASM_API (bool_t, font_copy_coords) (HB_WASM_EXEC_ENV
+ ptr_d(font_t, font),
+ ptr_d(coords_t, coords));
+
+HB_WASM_API (bool_t, font_set_coords) (HB_WASM_EXEC_ENV
+ ptr_d(font_t, font),
+ ptr_d(coords_t, coords));
+
+/* outline */
+
+enum glyph_outline_point_type_t
+{
+ MOVE_TO,
+ LINE_TO,
+ QUADRATIC_TO,
+ CUBIC_TO,
+};
+
+typedef struct
+{
+ float x;
+ float y;
+ uint32_t type;
+} glyph_outline_point_t;
+
+typedef struct
+{
+ uint32_t n_points;
+ ptr_t(glyph_outline_point_t) points;
+ uint32_t n_contours;
+ ptr_t(uint32_t) contours;
+} glyph_outline_t;
+#define GLYPH_OUTLINE_INIT {0, 0, 0, 0}
+
+HB_WASM_API (void, glyph_outline_free) (HB_WASM_EXEC_ENV
+ ptr_d(glyph_outline_t, outline));
+
+HB_WASM_API (bool_t, font_copy_glyph_outline) (HB_WASM_EXEC_ENV
+ ptr_d(font_t, font),
+ codepoint_t glyph,
+ ptr_d(glyph_outline_t, outline));
+
+
+/* shape */
+
+typedef struct
+{
+ tag_t tag;
+ uint32_t value;
+ uint32_t start;
+ uint32_t end;
+} feature_t;
+#define FEATURE_GLOBAL_START 0
+#define FEATURE_GLOBAL_END ((uint32_t) -1)
+
+HB_WASM_API (bool_t, shape_with) (HB_WASM_EXEC_ENV
+ ptr_d(font_t, font),
+ ptr_d(buffer_t, buffer),
+ ptr_d(const feature_t, features),
+ uint32_t num_features,
+ const char *shaper);
+
+/* Implement these in your shaper. */
+
+HB_WASM_INTERFACE (ptr_t(void), shape_plan_create) (ptr_d(face_t, face));
+
+HB_WASM_INTERFACE (bool_t, shape) (ptr_d(void, shape_plan),
+ ptr_d(font_t, font),
+ ptr_d(buffer_t, buffer),
+ ptr_d(const feature_t, features),
+ uint32_t num_features);
+
+HB_WASM_INTERFACE (void, shape_plan_destroy) (ptr_d(void, shape_plan));
+
+
+HB_WASM_END_DECLS
+
+#endif /* HB_WASM_API_H */
diff --git a/src/hb-wasm-api.hh b/src/hb-wasm-api.hh
new file mode 100644
index 0000000..4ead01c
--- /dev/null
+++ b/src/hb-wasm-api.hh
@@ -0,0 +1,117 @@
+/*
+ * Copyright © 2023 Behdad Esfahbod
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef HB_WASM_API_HH
+#define HB_WASM_API_HH
+
+#include "hb.hh"
+
+#include <wasm_export.h>
+
+#define HB_WASM_BEGIN_DECLS namespace hb { namespace wasm {
+#define HB_WASM_END_DECLS }}
+
+#define HB_WASM_API(ret_t, name) HB_INTERNAL ret_t name
+#define HB_WASM_API_COMPOUND(ret_t, name) HB_INTERNAL void name
+
+#define HB_WASM_EXEC_ENV wasm_exec_env_t exec_env,
+#define HB_WASM_EXEC_ENV_COMPOUND wasm_exec_env_t exec_env, ptr_t() retptr,
+
+#define ptr_t(type_t) uint32_t
+#define ptr_d(type_t, name) uint32_t name##ptr
+
+#include "hb-wasm-api.h"
+
+#undef HB_WASM_BEGIN_DECLS
+#undef HB_WASM_END_DECLS
+
+
+enum {
+ hb_wasm_ref_type_none,
+ hb_wasm_ref_type_face,
+ hb_wasm_ref_type_font,
+ hb_wasm_ref_type_buffer,
+};
+
+HB_INTERNAL extern hb_user_data_key_t _hb_wasm_ref_type_key;
+
+#define nullref 0
+
+#define HB_REF2OBJ(obj) \
+ hb_##obj##_t *obj = nullptr; \
+ HB_STMT_START { \
+ (void) wasm_externref_ref2obj (obj##ptr, (void **) &obj); \
+ /* Check object type. */ \
+ /* This works because all our objects have the same hb_object_t layout. */ \
+ if (unlikely (hb_##obj##_get_user_data (obj, &_hb_wasm_ref_type_key) != \
+ (void *) hb_wasm_ref_type_##obj)) \
+ obj = hb_##obj##_get_empty (); \
+ } HB_STMT_END
+
+#define HB_OBJ2REF(obj) \
+ uint32_t obj##ref = nullref; \
+ HB_STMT_START { \
+ hb_##obj##_set_user_data (obj, &_hb_wasm_ref_type_key, \
+ (void *) hb_wasm_ref_type_##obj, \
+ nullptr, false); \
+ (void) wasm_externref_obj2ref (module_inst, obj, &obj##ref); \
+ } HB_STMT_END
+
+#define HB_RETURN_STRUCT(type, name) \
+ type *_name_ptr = nullptr; \
+ { \
+ if (likely (wasm_runtime_validate_app_addr (module_inst, \
+ retptr, sizeof (type)))) \
+ { \
+ _name_ptr = (type *) wasm_runtime_addr_app_to_native (module_inst, retptr); \
+ if (unlikely (!_name_ptr)) \
+ return; \
+ } \
+ } \
+ type &name = *_name_ptr
+
+#define HB_PTR_PARAM(type, name) \
+ type *name = nullptr; \
+ HB_STMT_START { \
+ if (likely (wasm_runtime_validate_app_addr (module_inst, \
+ name##ptr, sizeof (type)))) \
+ name = (type *) wasm_runtime_addr_app_to_native (module_inst, name##ptr); \
+ } HB_STMT_END
+
+#define HB_ARRAY_PARAM(type, name, length) \
+ type *name = nullptr; \
+ HB_STMT_START { \
+ if (likely (!hb_unsigned_mul_overflows (length, sizeof (type)) && \
+ wasm_runtime_validate_app_addr (module_inst, \
+ name##ptr, length * sizeof (type)))) \
+ name = (type *) wasm_runtime_addr_app_to_native (module_inst, name##ptr); \
+ } HB_STMT_END
+
+#define HB_ARRAY_APP2NATIVE(type, name, length) \
+ ((type *) (!hb_unsigned_mul_overflows (length, sizeof (type)) && \
+ validate_app_addr (name, (length) * sizeof (type)) ? \
+ addr_app_to_native (name) : nullptr))
+
+
+#endif /* HB_WASM_API_HH */
diff --git a/src/hb-wasm-shape.cc b/src/hb-wasm-shape.cc
new file mode 100644
index 0000000..79442e9
--- /dev/null
+++ b/src/hb-wasm-shape.cc
@@ -0,0 +1,470 @@
+/*
+ * Copyright © 2011 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#undef HB_DEBUG_WASM
+#define HB_DEBUG_WASM 1
+
+#include "hb-shaper-impl.hh"
+
+#ifdef HAVE_WASM
+
+/* Compile wasm-micro-runtime with:
+ *
+ * $ cmake -DWAMR_BUILD_MULTI_MODULE=1 -DWAMR_BUILD_REF_TYPES=1 -DWAMR_BUILD_FAST_JIT=1
+ * $ make
+ *
+ * If you manage to build a wasm shared module successfully and want to use it,
+ * do the following:
+ *
+ * - Add -DWAMR_BUILD_MULTI_MODULE=1 to your cmake build for wasm-micro-runtime,
+ *
+ * - Remove the #define HB_WASM_NO_MODULES line below,
+ *
+ * - Install your shared module with name ending in .wasm in
+ * $(prefix)/$(libdir)/harfbuzz/wasm/
+ *
+ * - Build your font's wasm code importing the shared modules with the desired
+ * name. This can be done eg.: __attribute__((import_module("graphite2")))
+ * before each symbol in the the shared-module's headers.
+ *
+ * - Try shaping your font and hope for the best...
+ *
+ * I haven't been able to get this to work since emcc's support for shared libraries
+ * requires support from the host that seems to be missing from wasm-micro-runtime?
+ */
+
+#include "hb-wasm-api.hh"
+#include "hb-wasm-api-list.hh"
+
+#ifndef HB_WASM_NO_MODULES
+#define HB_WASM_NO_MODULES
+#endif
+
+
+#ifndef HB_WASM_NO_MODULES
+static bool HB_UNUSED
+_hb_wasm_module_reader (const char *module_name,
+ uint8_t **p_buffer, uint32_t *p_size)
+{
+ char path[sizeof (HB_WASM_MODULE_DIR) + 64] = HB_WASM_MODULE_DIR "/";
+ strncat (path, module_name, sizeof (path) - sizeof (HB_WASM_MODULE_DIR) - 16);
+ strncat (path, ".wasm", 6);
+
+ auto *blob = hb_blob_create_from_file (path);
+
+ unsigned length;
+ auto *data = hb_blob_get_data (blob, &length);
+
+ *p_buffer = (uint8_t *) hb_malloc (length);
+
+ if (length && !p_buffer)
+ return false;
+
+ memcpy (*p_buffer, data, length);
+ *p_size = length;
+
+ hb_blob_destroy (blob);
+
+ return true;
+}
+
+static void HB_UNUSED
+_hb_wasm_module_destroyer (uint8_t *buffer, uint32_t size)
+{
+ hb_free (buffer);
+}
+#endif
+
+/*
+ * shaper face data
+ */
+
+#define HB_WASM_TAG_WASM HB_TAG('W','a','s','m')
+
+struct hb_wasm_shape_plan_t {
+ wasm_module_inst_t module_inst;
+ wasm_exec_env_t exec_env;
+ ptr_d(void, wasm_shape_plan);
+};
+
+struct hb_wasm_face_data_t {
+ hb_blob_t *wasm_blob;
+ wasm_module_t wasm_module;
+ mutable hb_atomic_ptr_t<hb_wasm_shape_plan_t> plan;
+};
+
+static bool
+_hb_wasm_init ()
+{
+ /* XXX
+ *
+ * Umm. Make this threadsafe. How?!
+ * It's clunky that we can't allocate a static mutex.
+ * So we have to first allocate one on the heap atomically...
+ *
+ * Do we also need to lock around module creation?
+ *
+ * Also, wasm-micro-runtime uses a singleton instance. So if
+ * another library or client uses it, all bets are off. :-(
+ * If nothing else, around HB_REF2OBJ().
+ */
+
+ static bool initialized;
+ if (initialized)
+ return true;
+
+ RuntimeInitArgs init_args;
+ hb_memset (&init_args, 0, sizeof (RuntimeInitArgs));
+
+ init_args.mem_alloc_type = Alloc_With_Allocator;
+ init_args.mem_alloc_option.allocator.malloc_func = (void *) hb_malloc;
+ init_args.mem_alloc_option.allocator.realloc_func = (void *) hb_realloc;
+ init_args.mem_alloc_option.allocator.free_func = (void *) hb_free;
+
+ // Native symbols need below registration phase
+ init_args.n_native_symbols = ARRAY_LENGTH (_hb_wasm_native_symbols);
+ init_args.native_module_name = "env";
+ init_args.native_symbols = _hb_wasm_native_symbols;
+
+ if (unlikely (!wasm_runtime_full_init (&init_args)))
+ {
+ DEBUG_MSG (WASM, nullptr, "Init runtime environment failed.");
+ return false;
+ }
+
+#ifndef HB_WASM_NO_MODULES
+ wasm_runtime_set_module_reader (_hb_wasm_module_reader,
+ _hb_wasm_module_destroyer);
+#endif
+
+ initialized = true;
+ return true;
+}
+
+hb_wasm_face_data_t *
+_hb_wasm_shaper_face_data_create (hb_face_t *face)
+{
+ char error[128];
+ hb_wasm_face_data_t *data = nullptr;
+ hb_blob_t *wasm_blob = nullptr;
+ wasm_module_t wasm_module = nullptr;
+
+ wasm_blob = hb_face_reference_table (face, HB_WASM_TAG_WASM);
+ unsigned length = hb_blob_get_length (wasm_blob);
+ if (!length)
+ goto fail;
+
+ if (!_hb_wasm_init ())
+ goto fail;
+
+ wasm_module = wasm_runtime_load ((uint8_t *) hb_blob_get_data_writable (wasm_blob, nullptr),
+ length, error, sizeof (error));
+ if (unlikely (!wasm_module))
+ {
+ DEBUG_MSG (WASM, nullptr, "Load wasm module failed: %s", error);
+ goto fail;
+ }
+
+ data = (hb_wasm_face_data_t *) hb_calloc (1, sizeof (hb_wasm_face_data_t));
+ if (unlikely (!data))
+ goto fail;
+
+ data->wasm_blob = wasm_blob;
+ data->wasm_module = wasm_module;
+
+ return data;
+
+fail:
+ if (wasm_module)
+ wasm_runtime_unload (wasm_module);
+ hb_blob_destroy (wasm_blob);
+ hb_free (data);
+ return nullptr;
+}
+
+static hb_wasm_shape_plan_t *
+acquire_shape_plan (hb_face_t *face,
+ const hb_wasm_face_data_t *face_data)
+{
+ char error[128];
+
+ /* Fetch cached one if available. */
+ hb_wasm_shape_plan_t *plan = face_data->plan.get_acquire ();
+ if (likely (plan && face_data->plan.cmpexch (plan, nullptr)))
+ return plan;
+
+ plan = (hb_wasm_shape_plan_t *) hb_calloc (1, sizeof (hb_wasm_shape_plan_t));
+
+ wasm_module_inst_t module_inst = nullptr;
+ wasm_exec_env_t exec_env = nullptr;
+ wasm_function_inst_t func = nullptr;
+
+ constexpr uint32_t stack_size = 32 * 1024, heap_size = 2 * 1024 * 1024;
+
+ module_inst = plan->module_inst = wasm_runtime_instantiate (face_data->wasm_module,
+ stack_size, heap_size,
+ error, sizeof (error));
+ if (unlikely (!module_inst))
+ {
+ DEBUG_MSG (WASM, face_data, "Create wasm module instance failed: %s", error);
+ goto fail;
+ }
+
+ exec_env = plan->exec_env = wasm_runtime_create_exec_env (module_inst,
+ stack_size);
+ if (unlikely (!exec_env)) {
+ DEBUG_MSG (WASM, face_data, "Create wasm execution environment failed.");
+ goto fail;
+ }
+
+ func = wasm_runtime_lookup_function (module_inst, "shape_plan_create", nullptr);
+ if (func)
+ {
+ wasm_val_t results[1];
+ wasm_val_t arguments[1];
+
+ HB_OBJ2REF (face);
+ if (unlikely (!faceref))
+ {
+ DEBUG_MSG (WASM, face_data, "Failed to register face object.");
+ goto fail;
+ }
+
+ results[0].kind = WASM_I32;
+ arguments[0].kind = WASM_I32;
+ arguments[0].of.i32 = faceref;
+ bool ret = wasm_runtime_call_wasm_a (exec_env, func,
+ ARRAY_LENGTH (results), results,
+ ARRAY_LENGTH (arguments), arguments);
+
+ if (unlikely (!ret))
+ {
+ DEBUG_MSG (WASM, module_inst, "Calling shape_plan_create() failed: %s",
+ wasm_runtime_get_exception (module_inst));
+ goto fail;
+ }
+ plan->wasm_shape_planptr = results[0].of.i32;
+ }
+
+ return plan;
+
+fail:
+
+ if (exec_env)
+ wasm_runtime_destroy_exec_env (exec_env);
+ if (module_inst)
+ wasm_runtime_deinstantiate (module_inst);
+ hb_free (plan);
+ return nullptr;
+}
+
+static void
+release_shape_plan (const hb_wasm_face_data_t *face_data,
+ hb_wasm_shape_plan_t *plan,
+ bool cache = false)
+{
+ if (cache && face_data->plan.cmpexch (nullptr, plan))
+ return;
+
+ auto *module_inst = plan->module_inst;
+ auto *exec_env = plan->exec_env;
+
+ /* Is there even any point to having a shape_plan_destroy function
+ * and calling it? */
+ if (plan->wasm_shape_planptr)
+ {
+
+ auto *func = wasm_runtime_lookup_function (module_inst, "shape_plan_destroy", nullptr);
+ if (func)
+ {
+ wasm_val_t arguments[1];
+
+ arguments[0].kind = WASM_I32;
+ arguments[0].of.i32 = plan->wasm_shape_planptr;
+ bool ret = wasm_runtime_call_wasm_a (exec_env, func,
+ 0, nullptr,
+ ARRAY_LENGTH (arguments), arguments);
+
+ if (unlikely (!ret))
+ {
+ DEBUG_MSG (WASM, module_inst, "Calling shape_plan_destroy() failed: %s",
+ wasm_runtime_get_exception (module_inst));
+ }
+ }
+ }
+
+ wasm_runtime_destroy_exec_env (exec_env);
+ wasm_runtime_deinstantiate (module_inst);
+ hb_free (plan);
+}
+
+void
+_hb_wasm_shaper_face_data_destroy (hb_wasm_face_data_t *data)
+{
+ if (data->plan.get_relaxed ())
+ release_shape_plan (data, data->plan);
+ wasm_runtime_unload (data->wasm_module);
+ hb_blob_destroy (data->wasm_blob);
+ hb_free (data);
+}
+
+
+/*
+ * shaper font data
+ */
+
+struct hb_wasm_font_data_t {};
+
+hb_wasm_font_data_t *
+_hb_wasm_shaper_font_data_create (hb_font_t *font HB_UNUSED)
+{
+ return (hb_wasm_font_data_t *) HB_SHAPER_DATA_SUCCEEDED;
+}
+
+void
+_hb_wasm_shaper_font_data_destroy (hb_wasm_font_data_t *data HB_UNUSED)
+{
+}
+
+
+/*
+ * shaper
+ */
+
+hb_bool_t
+_hb_wasm_shape (hb_shape_plan_t *shape_plan,
+ hb_font_t *font,
+ hb_buffer_t *buffer,
+ const hb_feature_t *features,
+ unsigned int num_features)
+{
+ if (unlikely (buffer->in_error ()))
+ return false;
+
+ bool ret = true;
+ hb_face_t *face = font->face;
+ const hb_wasm_face_data_t *face_data = face->data.wasm;
+
+ bool retried = false;
+ if (0)
+ {
+retry:
+ DEBUG_MSG (WASM, font, "Retrying...");
+ }
+
+ wasm_function_inst_t func = nullptr;
+
+ hb_wasm_shape_plan_t *plan = acquire_shape_plan (face, face_data);
+ if (unlikely (!plan))
+ {
+ DEBUG_MSG (WASM, face_data, "Acquiring shape-plan failed.");
+ return false;
+ }
+
+ auto *module_inst = plan->module_inst;
+ auto *exec_env = plan->exec_env;
+
+ HB_OBJ2REF (font);
+ HB_OBJ2REF (buffer);
+ if (unlikely (!fontref || !bufferref))
+ {
+ DEBUG_MSG (WASM, module_inst, "Failed to register objects.");
+ goto fail;
+ }
+
+ func = wasm_runtime_lookup_function (module_inst, "shape", nullptr);
+ if (unlikely (!func))
+ {
+ DEBUG_MSG (WASM, module_inst, "Shape function not found.");
+ goto fail;
+ }
+
+ wasm_val_t results[1];
+ wasm_val_t arguments[5];
+
+ results[0].kind = WASM_I32;
+ arguments[0].kind = WASM_I32;
+ arguments[0].of.i32 = plan->wasm_shape_planptr;
+ arguments[1].kind = WASM_I32;
+ arguments[1].of.i32 = fontref;
+ arguments[2].kind = WASM_I32;
+ arguments[2].of.i32 = bufferref;
+ arguments[3].kind = WASM_I32;
+ arguments[3].of.i32 = num_features ? wasm_runtime_module_dup_data (module_inst,
+ (const char *) features,
+ num_features * sizeof (features[0])) : 0;
+ arguments[4].kind = WASM_I32;
+ arguments[4].of.i32 = num_features;
+
+ ret = wasm_runtime_call_wasm_a (exec_env, func,
+ ARRAY_LENGTH (results), results,
+ ARRAY_LENGTH (arguments), arguments);
+
+ if (num_features)
+ wasm_runtime_module_free (module_inst, arguments[2].of.i32);
+
+ if (unlikely (!ret || !results[0].of.i32))
+ {
+ DEBUG_MSG (WASM, module_inst, "Calling shape() failed: %s",
+ wasm_runtime_get_exception (module_inst));
+ if (!buffer->ensure_unicode ())
+ {
+ DEBUG_MSG (WASM, font, "Shape failed but buffer is not in Unicode; failing...");
+ goto fail;
+ }
+ if (retried)
+ {
+ DEBUG_MSG (WASM, font, "Giving up...");
+ goto fail;
+ }
+ buffer->successful = true;
+ retried = true;
+ release_shape_plan (face_data, plan);
+ plan = nullptr;
+ goto retry;
+ }
+
+ /* TODO Regularize clusters according to direction & cluster level,
+ * such that client doesn't crash with unmet expectations. */
+
+ if (!results[0].of.i32)
+ {
+fail:
+ ret = false;
+ }
+
+ release_shape_plan (face_data, plan, ret);
+
+ if (ret)
+ {
+ buffer->clear_glyph_flags ();
+ buffer->unsafe_to_break ();
+ }
+
+ return ret;
+}
+
+#endif
diff --git a/src/hb.hh b/src/hb.hh
index 205f8cf..972608d 100644
--- a/src/hb.hh
+++ b/src/hb.hh
@@ -64,6 +64,7 @@
#pragma GCC diagnostic error "-Wbitwise-instead-of-logical"
#pragma GCC diagnostic error "-Wcast-align"
#pragma GCC diagnostic error "-Wcast-function-type"
+#pragma GCC diagnostic error "-Wconstant-conversion"
#pragma GCC diagnostic error "-Wcomma"
#pragma GCC diagnostic error "-Wdelete-non-virtual-dtor"
#pragma GCC diagnostic error "-Wembedded-directive"
@@ -315,6 +316,14 @@
#define __restrict
#endif
+#ifndef HB_ALWAYS_INLINE
+#if defined(_MSC_VER)
+#define HB_ALWAYS_INLINE __forceinline
+#else
+#define HB_ALWAYS_INLINE __attribute__((always_inline)) inline
+#endif
+#endif
+
/*
* Borrowed from https://bugzilla.mozilla.org/show_bug.cgi?id=1215411
* HB_FALLTHROUGH is an annotation to suppress compiler warnings about switch
diff --git a/src/main.cc b/src/main.cc
index edc9872..4af9569 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -245,7 +245,7 @@
if (hb_color_get_alpha (color) != 255)
fprintf (f, "fill-opacity=\"%.3f\"", (double) hb_color_get_alpha (color) / 255.);
fprintf (f, "d=\"");
- hb_font_get_glyph_shape (font, layers[layer].glyph, funcs, &draw_data);
+ hb_font_draw_glyph (font, layers[layer].glyph, funcs, &draw_data);
fprintf (f, "\"/>\n");
}
@@ -284,7 +284,7 @@
draw_data_t draw_data;
draw_data.ascender = font_extents.ascender;
draw_data.f = f;
- hb_font_get_glyph_shape (font, gid, funcs, &draw_data);
+ hb_font_draw_glyph (font, gid, funcs, &draw_data);
fprintf (f, "\"/></svg>");
fclose (f);
}
diff --git a/src/meson.build b/src/meson.build
index 90735cb..4d63ecf 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -324,6 +324,19 @@
hb_graphite2_sources = files('hb-graphite2.cc')
hb_graphite2_headers = files('hb-graphite2.h')
+hb_wasm_sources = files(
+ 'hb-wasm-api.cc',
+ 'hb-wasm-api.hh',
+ 'hb-wasm-api-blob.hh',
+ 'hb-wasm-api-buffer.hh',
+ 'hb-wasm-api-common.hh',
+ 'hb-wasm-api-face.hh',
+ 'hb-wasm-api-font.hh',
+ 'hb-wasm-api-shape.hh',
+ 'hb-wasm-shape.cc',
+)
+hb_wasm_headers = files()
+
# System-dependent sources and headers
hb_coretext_sources = files('hb-coretext.cc')
@@ -353,11 +366,10 @@
'hb-subset-cff-common.cc',
'hb-subset-cff-common.hh',
'hb-subset-cff1.cc',
- 'hb-subset-cff1.hh',
'hb-subset-cff2.cc',
- 'hb-subset-cff2.hh',
'hb-subset-input.cc',
'hb-subset-input.hh',
+ 'hb-subset-instancer-solver.hh',
'hb-subset-instancer-solver.cc',
'hb-subset-plan.cc',
'hb-subset-plan.hh',
@@ -417,7 +429,7 @@
output: 'harfbuzz.cc',
input: hb_base_sources + hb_glib_sources + hb_ft_sources +
hb_graphite2_sources + hb_uniscribe_sources + hb_gdi_sources +
- hb_directwrite_sources + hb_coretext_sources,
+ hb_directwrite_sources + hb_coretext_sources + hb_wasm_sources,
command: [find_program('gen-harfbuzzcc.py'),
'@OUTPUT@', meson.current_source_dir(), '@INPUT@'],
)
@@ -460,6 +472,13 @@
harfbuzz_deps += [graphite2_dep, graphite_dep]
endif
+if conf.get('HAVE_WASM', 0) == 1
+ hb_sources += hb_wasm_sources
+ hb_headers += hb_wasm_headers
+ harfbuzz_deps += wasm_dep
+ #harfbuzz_deps += llvm_dep
+endif
+
if conf.get('HAVE_UNISCRIBE', 0) == 1
hb_sources += hb_uniscribe_sources
hb_headers += hb_uniscribe_headers
@@ -497,6 +516,7 @@
'GRAPHITE',
'ICU',
'UNISCRIBE',
+ 'WASM',
]
hb_enabled_features = configuration_data()
@@ -700,6 +720,8 @@
'test-unicode-ranges': ['test-unicode-ranges.cc'],
'test-vector': ['test-vector.cc', 'hb-static.cc'],
'test-bimap': ['test-bimap.cc', 'hb-static.cc'],
+ 'test-instancer-solver': ['test-subset-instancer-solver.cc', 'hb-subset-instancer-solver.cc', 'hb-static.cc'],
+ 'test-tuple-varstore': ['test-tuple-varstore.cc', 'hb-subset-instancer-solver.cc', 'hb-static.cc'],
}
foreach name, source : compiled_tests
if cpp.get_argument_syntax() == 'msvc' and source.contains('hb-static.cc')
@@ -846,7 +868,7 @@
if build_gir
conf.set('HAVE_INTROSPECTION', 1)
- hb_gen_files_gir = gnome.generate_gir(libharfbuzz_gobject,
+ hb_gen_files_gir = gnome.generate_gir([libharfbuzz_gobject, libharfbuzz],
sources: [gir_headers, gir_sources, gobject_enums_h],
dependencies: libharfbuzz_dep,
namespace: 'HarfBuzz',
@@ -854,7 +876,7 @@
identifier_prefix: 'hb_',
symbol_prefix: ['hb', 'hb_gobject'],
includes: ['GObject-2.0', 'freetype2-2.0'],
- export_packages: ['harfbuzz-gobject'],
+ export_packages: ['harfbuzz-gobject', 'harfbuzz'],
header: 'hb-gobject.h',
install: true,
extra_args: ['--cflags-begin',
@@ -910,7 +932,10 @@
env.set('HBHEADERS', ' '.join(HBHEADERS))
if cpp.get_argument_syntax() != 'msvc' and not meson.is_cross_build() # ensure the local tools are usable
- dist_check_script += ['check-libstdc++', 'check-static-inits', 'check-symbols']
+ dist_check_script += ['check-static-inits', 'check-symbols']
+ if get_option('wasm').disabled()
+ dist_check_script += ['check-libstdc++']
+ endif
endif
foreach name : dist_check_script
diff --git a/src/test-gsub-get-alternates.cc b/src/test-gsub-get-alternates.cc
index c9f3b4e..555440c 100644
--- a/src/test-gsub-get-alternates.cc
+++ b/src/test-gsub-get-alternates.cc
@@ -65,7 +65,7 @@
for (unsigned i = 0; i < count; i++)
{
unsigned alt_count = 0;
- for (unsigned lookup_index = HB_SET_VALUE_INVALID;
+ for (hb_codepoint_t lookup_index = HB_SET_VALUE_INVALID;
hb_set_next (lookup_indexes, &lookup_index);)
if ((alt_count = hb_ot_layout_lookup_get_glyph_alternates (face,
lookup_index,
diff --git a/src/test-map.cc b/src/test-map.cc
index 61cc8bf..cb722eb 100644
--- a/src/test-map.cc
+++ b/src/test-map.cc
@@ -79,7 +79,7 @@
s.set (1, 2);
s.set (3, 4);
- hb_vector_t<hb_pair_t<hb_codepoint_t, hb_codepoint_t>> v (s);
+ hb_vector_t<hb_codepoint_pair_t> v (s);
hb_map_t v0 (v);
hb_map_t v1 (s);
hb_map_t v2 (std::move (s));
@@ -112,7 +112,7 @@
/* Test initializing from initializer list and swapping. */
{
- using pair_t = hb_pair_t<hb_codepoint_t, hb_codepoint_t>;
+ using pair_t = hb_codepoint_pair_t;
hb_map_t v1 {pair_t{1,2}, pair_t{4,5}};
hb_map_t v2 {pair_t{3,4}};
hb_swap (v1, v2);
@@ -149,7 +149,7 @@
/* Test hashing maps. */
{
- using pair = hb_pair_t<hb_codepoint_t, hb_codepoint_t>;
+ using pair = hb_codepoint_pair_t;
hb_hashmap_t<hb_map_t, hb_map_t> m1;
diff --git a/src/test-set.cc b/src/test-set.cc
index 5f13ab3..853aed4 100644
--- a/src/test-set.cc
+++ b/src/test-set.cc
@@ -91,7 +91,7 @@
s << 12;
/* Sink a range. */
- s << hb_pair_t<hb_codepoint_t, hb_codepoint_t> {1, 3};
+ s << hb_codepoint_pair_t {1, 3};
hb_set_t v (hb_iter (s));
diff --git a/src/test-subset-instancer-solver.cc b/src/test-subset-instancer-solver.cc
new file mode 100644
index 0000000..ca473d9
--- /dev/null
+++ b/src/test-subset-instancer-solver.cc
@@ -0,0 +1,395 @@
+/*
+ * Copyright © 2023 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Qunxin Liu
+ */
+
+#include "hb-subset-instancer-solver.hh"
+
+static inline bool approx (Triple a, Triple b)
+{
+ return fabsf (a.minimum - b.minimum) < 0.000001f &&
+ fabsf (a.middle - b.middle) < 0.000001f &&
+ fabsf (a.maximum - b.maximum) < 0.000001f;
+}
+
+static inline bool approx (float a, float b)
+{ return fabsf (a - b) < 0.000001f; }
+
+/* tests ported from
+ * https://github.com/fonttools/fonttools/blob/main/Tests/varLib/instancer/solver_test.py */
+int
+main (int argc, char **argv)
+{
+ /* Case 1 */
+ {
+ /* pin axis*/
+ Triple tent (0.f, 1.f, 1.f);
+ Triple axis_range (0.f, 0.f, 0.f);
+ result_t out = rebase_tent (tent, axis_range);
+ assert (out.length == 0);
+ }
+
+ {
+ /* pin axis*/
+ Triple tent (0.f, 1.f, 1.f);
+ Triple axis_range (0.5f, 0.5f, 0.5f);
+ result_t out = rebase_tent (tent, axis_range);
+ assert (out.length == 1);
+ assert (out[0].first == 0.5f);
+ assert (out[0].second == Triple ());
+ }
+
+ {
+ /* tent falls outside the new axis range */
+ Triple tent (0.3f, 0.5f, 0.8f);
+ Triple axis_range (0.1f, 0.2f, 0.3f);
+ result_t out = rebase_tent (tent, axis_range);
+ assert (out.length == 0);
+ }
+
+ /* Case 2 */
+ {
+ Triple tent (0.f, 1.f, 1.f);
+ Triple axis_range (-1.f, 0.f, 0.5f);
+ result_t out = rebase_tent (tent, axis_range);
+ assert (out.length == 1);
+ assert (out[0].first == 0.5f);
+ assert (out[0].second == Triple (0.f, 1.f, 1.f));
+ }
+
+ /* Case 2 */
+ {
+ Triple tent (0.f, 1.f, 1.f);
+ Triple axis_range (-1.f, 0.f, 0.75f);
+ result_t out = rebase_tent (tent, axis_range);
+ assert (out.length == 1);
+ assert (out[0].first == 0.75f);
+ assert (out[0].second == Triple (0.f, 1.f, 1.f));
+ }
+
+ /* Without gain: */
+ /* Case 3 */
+ {
+ Triple tent (0.f, 0.2f, 1.f);
+ Triple axis_range (-1.f, 0.f, 0.8f);
+ result_t out = rebase_tent (tent, axis_range);
+ assert (out.length == 1);
+ assert (out[0].first == 1.f);
+ assert (out[0].second == Triple (0.f, 0.25f, 1.25f));
+ }
+
+ /* Case 3 boundary */
+ {
+ Triple tent (0.f, 0.4f, 1.f);
+ Triple axis_range (-1.f, 0.f, 0.5f);
+ result_t out = rebase_tent (tent, axis_range);
+ assert (out.length == 1);
+ assert (out[0].first == 1.f);
+ assert (out[0].second == Triple (0.f, 0.8f, 32767/(float) (1 << 14)));
+ }
+
+ /* Case 4 */
+ {
+ Triple tent (0.f, 0.25f, 1.f);
+ Triple axis_range (-1.f, 0.f, 0.4f);
+ result_t out = rebase_tent (tent, axis_range);
+ assert (out.length == 2);
+ assert (out[0].first == 1.f);
+ assert (out[0].second == Triple (0.f, 0.625f, 1.f));
+ assert (approx (out[1].first, 0.8f));
+ assert (out[1].second == Triple (0.625f, 1.f, 1.f));
+ }
+
+ /* Case 4 */
+ {
+ Triple tent (0.25f, 0.3f, 1.05f);
+ Triple axis_range (0.f, 0.2f, 0.4f);
+ result_t out = rebase_tent (tent, axis_range);
+ assert (out.length == 2);
+ assert (out[0].first == 1.f);
+ assert (approx (out[0].second, Triple (0.25f, 0.5f, 1.f)));
+ assert (approx (out[1].first, 2.6f/3));
+ assert (approx (out[1].second, Triple (0.5f, 1.f, 1.f)));
+ }
+
+ /* Case 4 boundary */
+ {
+ Triple tent (0.25f, 0.5f, 1.f);
+ Triple axis_range (0.f, 0.25f, 0.5f);
+ result_t out = rebase_tent (tent, axis_range);
+ assert (out.length == 1);
+ assert (out[0].first == 1.f);
+ assert (out[0].second == Triple (0.f, 1.f, 1.f));
+ }
+
+ /* With gain */
+ /* Case 3a/1neg */
+ {
+ Triple tent (0.f, 0.5f, 1.f);
+ Triple axis_range (0.f, 0.5f, 1.f);
+ result_t out = rebase_tent (tent, axis_range);
+ assert (out.length == 3);
+ assert (out[0].first == 1.f);
+ assert (out[0].second == Triple ());
+ assert (out[1].first == -1.f);
+ assert (out[1].second == Triple (0.f, 1.f, 1.f));
+ assert (out[2].first == -1.f);
+ assert (out[2].second == Triple (-1.f, -1.f, 0.f));
+ }
+
+ {
+ Triple tent (0.f, 0.5f, 1.f);
+ Triple axis_range (0.f, 0.5f, 0.75f);
+ result_t out = rebase_tent (tent, axis_range);
+ assert (out.length == 3);
+ assert (out[0].first == 1.f);
+ assert (out[0].second == Triple ());
+ assert (out[1].first == -0.5f);
+ assert (out[1].second == Triple (0.f, 1.f, 1.f));
+ assert (out[2].first == -1.f);
+ assert (out[2].second == Triple (-1.f, -1.f, 0.f));
+ }
+
+ {
+ Triple tent (0.f, 0.5f, 1.f);
+ Triple axis_range (0.f, 0.25f, 0.8f);
+ result_t out = rebase_tent (tent, axis_range);
+ assert (out.length == 4);
+ assert (out[0].first == 0.5f);
+ assert (out[0].second == Triple ());
+ assert (out[1].first == 0.5f);
+ assert (approx (out[1].second, Triple (0.f, 0.454545f, 0.909091f)));
+ assert (approx (out[2].first, -0.1f));
+ assert (approx (out[2].second, Triple (0.909091f, 1.f, 1.f)));
+ assert (out[3].first == -0.5f);
+ assert (out[3].second == Triple (-1.f, -1.f, 0.f));
+ }
+
+ /* Case 3a/1neg */
+ {
+ Triple tent (0.f, 0.5f, 2.f);
+ Triple axis_range (0.2f, 0.5f, 0.8f);
+ result_t out = rebase_tent (tent, axis_range);
+ assert (out.length == 3);
+ assert (out[0].first == 1.f);
+ assert (out[0].second == Triple ());
+ assert (approx (out[1].first, -0.2f));
+ assert (out[1].second == Triple (0.f, 1.f, 1.f));
+ assert (approx (out[2].first, -0.6f));
+ assert (out[2].second == Triple (-1.f, -1.f, 0.f));
+ }
+
+ /* Case 3a/1neg */
+ {
+ Triple tent (0.f, 0.5f, 2.f);
+ Triple axis_range (0.2f, 0.5f, 1.f);
+ result_t out = rebase_tent (tent, axis_range);
+ assert (out.length == 3);
+ assert (out[0].first == 1.f);
+ assert (out[0].second == Triple ());
+ assert (approx (out[1].first, -1.f/3));
+ assert (out[1].second == Triple (0.f, 1.f, 1.f));
+ assert (approx (out[2].first, -0.6f));
+ assert (out[2].second == Triple (-1.f, -1.f, 0.f));
+ }
+
+ /* Case 3 */
+ {
+ Triple tent (0.f, 0.5f, 1.f);
+ Triple axis_range (0.25f, 0.25f, 0.75f);
+ result_t out = rebase_tent (tent, axis_range);
+ assert (out.length == 2);
+ assert (out[0].first == 0.5f);
+ assert (out[0].second == Triple ());
+ assert (out[1].first == 0.5f);
+ assert (out[1].second == Triple (0.f, 0.5f, 1.0f));
+ }
+
+ /* Case 1neg */
+ {
+ Triple tent (0.f, 0.5f, 1.f);
+ Triple axis_range (0.f, 0.25f, 0.5f);
+ result_t out = rebase_tent (tent, axis_range);
+ assert (out.length == 3);
+ assert (out[0].first == 0.5f);
+ assert (out[0].second == Triple ());
+ assert (out[1].first == 0.5f);
+ assert (out[1].second == Triple (0.f, 1.f, 1.f));
+ assert (out[2].first == -0.5f);
+ assert (out[2].second == Triple (-1.f, -1.f, 0.f));
+ }
+
+ /* Case 2neg */
+ {
+ Triple tent (0.05f, 0.55f, 1.f);
+ Triple axis_range (0.f, 0.25f, 0.5f);
+ result_t out = rebase_tent (tent, axis_range);
+ assert (out.length == 4);
+ assert (approx (out[0].first, 0.4f));
+ assert (out[0].second == Triple ());
+ assert (approx (out[1].first, 0.5f));
+ assert (out[1].second == Triple (0.f, 1.f, 1.f));
+ assert (approx (out[2].first, -0.4f));
+ assert (out[2].second == Triple (-1.f, -0.8f, 0.f));
+ assert (approx (out[3].first, -0.4f));
+ assert (out[3].second == Triple (-1.f, -1.f, -0.8f));
+ }
+
+ /* Case 2neg, other side */
+ {
+ Triple tent (-1.f, -0.55f, -0.05f);
+ Triple axis_range (-0.5f, -0.25f, 0.f);
+ result_t out = rebase_tent (tent, axis_range);
+ assert (out.length == 4);
+ assert (approx (out[0].first, 0.4f));
+ assert (out[0].second == Triple ());
+ assert (approx (out[1].first, 0.5f));
+ assert (out[1].second == Triple (-1.f, -1.f, 0.f));
+ assert (approx (out[2].first, -0.4f));
+ assert (out[2].second == Triple (0.f, 0.8f, 1.f));
+ assert (approx (out[3].first, -0.4f));
+ assert (out[3].second == Triple (0.8f, 1.f, 1.f));
+ }
+
+ /* Misc corner cases */
+ {
+ Triple tent (0.5f, 0.5f, 0.5f);
+ Triple axis_range (0.5f, 0.5f, 0.5f);
+ result_t out = rebase_tent (tent, axis_range);
+ assert (out.length == 1);
+ assert (out[0].first == 1.f);
+ assert (out[0].second == Triple ());
+ }
+
+ {
+ Triple tent (0.3f, 0.5f, 0.7f);
+ Triple axis_range (0.1f, 0.5f, 0.9f);
+ result_t out = rebase_tent (tent, axis_range);
+ assert (out.length == 5);
+ assert (out[0].first == 1.f);
+ assert (out[0].second == Triple ());
+ assert (out[1].first == -1.f);
+ assert (out[1].second == Triple (0.f, 0.5f, 1.f));
+ assert (out[2].first == -1.f);
+ assert (out[2].second == Triple (0.5f, 1.f, 1.f));
+ assert (out[3].first == -1.f);
+ assert (approx (out[3].second, Triple (-1.f, -0.5f, 0.f)));
+ assert (out[4].first == -1.f);
+ assert (approx (out[4].second, Triple (-1.f, -1.f, -0.5f)));
+ }
+
+ {
+ Triple tent (0.5f, 0.5f, 0.5f);
+ Triple axis_range (0.25f, 0.25f, 0.5f);
+ result_t out = rebase_tent (tent, axis_range);
+ assert (out.length == 1);
+ assert (out[0].first == 1.f);
+ assert (out[0].second == Triple (1.f, 1.f, 1.f));
+ }
+
+ {
+ Triple tent (0.5f, 0.5f, 0.5f);
+ Triple axis_range (0.25f, 0.35f, 0.5f);
+ result_t out = rebase_tent (tent, axis_range);
+ assert (out.length == 1);
+ assert (out[0].first == 1.f);
+ assert (out[0].second == Triple (1.f, 1.f, 1.f));
+ }
+
+ {
+ Triple tent (0.5f, 0.5f, 0.55f);
+ Triple axis_range (0.25f, 0.35f, 0.5f);
+ result_t out = rebase_tent (tent, axis_range);
+ assert (out.length == 1);
+ assert (out[0].first == 1.f);
+ assert (out[0].second == Triple (1.f, 1.f, 1.f));
+ }
+
+ {
+ Triple tent (0.5f, 0.5f, 1.f);
+ Triple axis_range (0.5f, 0.5f, 1.f);
+ result_t out = rebase_tent (tent, axis_range);
+ assert (out.length == 2);
+ assert (out[0].first == 1.f);
+ assert (out[0].second == Triple ());
+ assert (out[1].first == -1.f);
+ assert (out[1].second == Triple (0.f, 1.f, 1.f));
+ }
+
+ {
+ Triple tent (0.25f, 0.5f, 1.f);
+ Triple axis_range (0.5f, 0.5f, 1.f);
+ result_t out = rebase_tent (tent, axis_range);
+ assert (out.length == 2);
+ assert (out[0].first == 1.f);
+ assert (out[0].second == Triple ());
+ assert (out[1].first == -1.f);
+ assert (out[1].second == Triple (0.f, 1.f, 1.f));
+ }
+
+ {
+ Triple tent (0.f, 0.2f, 1.f);
+ Triple axis_range (0.f, 0.f, 0.5f);
+ result_t out = rebase_tent (tent, axis_range);
+ assert (out.length == 1);
+ assert (out[0].first == 1.f);
+ assert (out[0].second == Triple (0.f, 0.4f, 32767/(float) (1 << 14)));
+ }
+
+
+ {
+ Triple tent (0.f, 0.5f, 1.f);
+ Triple axis_range (-1.f, 0.25f, 1.f);
+ result_t out = rebase_tent (tent, axis_range);
+ assert (out.length == 5);
+ assert (out[0].first == 0.5f);
+ assert (out[0].second == Triple ());
+ assert (out[1].first == 0.5f);
+ assert (out[1].second == Triple (0.f, 1.f/3, 2.f/3));
+ assert (out[2].first == -0.5f);
+ assert (out[2].second == Triple (2.f/3, 1.f, 1.f));
+ assert (out[3].first == -0.5f);
+ assert (out[3].second == Triple (-1.f, -0.2f, 0.f));
+ assert (out[4].first == -0.5f);
+ assert (out[4].second == Triple (-1.f, -1.f, -0.2f));
+ }
+
+ {
+ Triple tent (0.5f, 0.5f, 0.5f);
+ Triple axis_range (0.f, 0.5f, 1.f);
+ result_t out = rebase_tent (tent, axis_range);
+ assert (out.length == 5);
+ assert (out[0].first == 1.f);
+ assert (out[0].second == Triple ());
+ assert (out[1].first == -1.f);
+ assert (out[1].second == Triple (0.f, 2/(float) (1 << 14), 1.f));
+ assert (out[2].first == -1.f);
+ assert (out[2].second == Triple (2/(float) (1 << 14), 1.f, 1.f));
+ assert (out[3].first == -1.f);
+ assert (out[3].second == Triple (-1.f, -2/(float) (1 << 14), 0.f));
+ assert (out[4].first == -1.f);
+ assert (out[4].second == Triple (-1.f, -1.f, -2/(float) (1 << 14)));
+ }
+}
+
diff --git a/src/test-tuple-varstore.cc b/src/test-tuple-varstore.cc
new file mode 100644
index 0000000..c1fb747
--- /dev/null
+++ b/src/test-tuple-varstore.cc
@@ -0,0 +1,142 @@
+/*
+ * Copyright © 2020 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ */
+#include "hb-ot-var-cvar-table.hh"
+
+// cvar table data from Multi-ABC.ttf
+const char cvar_data[] = "\x0\x1\x0\x0\x0\x2\x0\x14\x0\x51\xa0\x0\xc0\x0\x0\x54\xa0\x0\x40\x0\x2a\x29\x17\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\xd\xff\x0\xfd\x1\x0\xff\x0\xfd\x1\x0\xdb\xdb\xe6\xe6\x82\x0\xfd\x84\x6\xfd\x0\x2\xe3\xe3\xec\xec\x82\x4\x1\xe3\xe3\xec\xec\x82\x0\x1\x2a\x29\x17\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\x1\xd\x1\x0\x5\xfd\x0\x1\x0\x5\xfd\x0\x61\x61\x44\x44\x82\x0\x5\x81\x9\x1\xff\x1\x7\xff\xfb\x49\x49\x35\x35\x82\x4\xff\x49\x49\x35\x35\x82\x0\xff";
+
+static void
+test_decompile_cvar ()
+{
+ const OT::cvar* cvar_table = reinterpret_cast<const OT::cvar*> (cvar_data);
+ unsigned point_count = 65;
+ unsigned axis_count = 1;
+
+ hb_tag_t axis_tag = HB_TAG ('w', 'g', 'h', 't');
+ hb_map_t axis_idx_tag_map;
+ axis_idx_tag_map.set (0, axis_tag);
+
+ OT::TupleVariationData::tuple_variations_t tuple_variations;
+ bool result = cvar_table->decompile_tuple_variations (axis_count, point_count, false, &axis_idx_tag_map, tuple_variations);
+ assert (result);
+ assert (tuple_variations.tuple_vars.length == 2);
+ for (unsigned i = 0; i < 2; i++)
+ {
+ assert (tuple_variations.tuple_vars[i].axis_tuples.get_population () == 1);
+ assert (!tuple_variations.tuple_vars[i].deltas_y);
+ assert (tuple_variations.tuple_vars[i].indices.length == 65);
+ assert (tuple_variations.tuple_vars[i].indices.length == tuple_variations.tuple_vars[i].deltas_x.length);
+ }
+ assert (tuple_variations.tuple_vars[0].axis_tuples.get (axis_tag) == Triple (-1.f, -1.f, 0.f));
+ assert (tuple_variations.tuple_vars[1].axis_tuples.get (axis_tag) == Triple (0.f, 1.f, 1.f));
+
+ hb_vector_t<float> deltas_1 {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, -1.f, 0.f, -3.f, 1.f, 0.f, -1.f, 0.f, -3.f, 1.f, 0.f, -37.f, -37.f, -26.f, -26.f, 0.f, 0.f, 0.f, -3.f, 0.f, 0.f, 0.f, 0.f, 0.f, -3.f, 0.f, 2.f, -29.f, -29.f, -20.f, -20.f, 0.f, 0.f, 0.f, 1.f, -29.f, -29.f, -20.f, -20.f, 0.f, 0.f, 0.f, 1.f};
+ for (unsigned i = 0; i < 65; i++)
+ {
+ if (i < 23)
+ assert (tuple_variations.tuple_vars[0].indices[i] == 0);
+ else
+ {
+ assert (tuple_variations.tuple_vars[0].indices[i] == 1);
+ assert (tuple_variations.tuple_vars[0].deltas_x[i] == deltas_1[i]);
+ }
+ }
+
+ hb_vector_t<float> deltas_2 {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 5.f, -3.f, 0.f, 1.f, 0.f, 5.f, -3.f, 0.f, 97.f, 97.f, 68.f, 68.f, 0.f, 0.f, 0.f, 5.f, 0.f, 0.f, 1.f, -1.f, 1.f, 7.f, -1.f, -5.f, 73.f, 73.f, 53.f, 53.f, 0.f, 0.f, 0.f, -1.f, 73.f, 73.f, 53.f, 53.f, 0.f, 0.f, 0.f, -1.f};
+ for (unsigned i = 0 ; i < 65; i++)
+ {
+ if (i < 23)
+ assert (tuple_variations.tuple_vars[1].indices[i] == 0);
+ else
+ {
+ assert (tuple_variations.tuple_vars[1].indices[i] == 1);
+ assert (tuple_variations.tuple_vars[1].deltas_x[i] == deltas_2[i]);
+ }
+ }
+
+ /* partial instancing wght=300:800 */
+ hb_hashmap_t<hb_tag_t, Triple> normalized_axes_location;
+ normalized_axes_location.set (axis_tag, Triple (-0.512817f, 0.f, 0.700012f));
+
+ tuple_variations.change_tuple_variations_axis_limits (&normalized_axes_location);
+ tuple_variations.merge_tuple_variations ();
+
+ assert (tuple_variations.tuple_vars[0].indices.length == 65);
+ assert (tuple_variations.tuple_vars[1].indices.length == 65);
+ assert (!tuple_variations.tuple_vars[0].deltas_y);
+ assert (!tuple_variations.tuple_vars[1].deltas_y);
+ assert (tuple_variations.tuple_vars[0].axis_tuples.get (axis_tag) == Triple (-1.f, -1.f, 0.f));
+ assert (tuple_variations.tuple_vars[1].axis_tuples.get (axis_tag) == Triple (0.f, 1.f, 1.f));
+
+ hb_vector_t<float> rounded_deltas_1 {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, -1, 0.f, -2, 1, 0.f, -1, 0.f, -2, 1, 0.f, -19, -19, -13, -13, 0.f, 0.f, 0.f, -2, 0.f, 0.f, 0.f, 0.f, 0.f, -2, 0.f, 1, -15, -15, -10.f, -10.f, 0.f, 0.f, 0.f, 1, -15, -15, -10.f, -10.f, 0.f, 0.f, 0.f, 1};
+
+ hb_vector_t<float> rounded_deltas_2 {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 1, 0.f, 4, -2, 0.f, 1, 0.f, 4, -2, 0.f, 68, 68, 48, 48, 0.f, 0.f, 0.f, 4, 0.f, 0.f, 1, -1, 1, 5, -1, -4, 51, 51, 37, 37, 0.f, 0.f, 0.f, -1, 51, 51, 37, 37, 0.f, 0.f, 0.f, -1};
+
+ for (unsigned i = 0; i < 65; i++)
+ {
+ if (i < 23)
+ {
+ assert (tuple_variations.tuple_vars[0].indices[i] == 0);
+ assert (tuple_variations.tuple_vars[1].indices[i] == 0);
+ }
+ else
+ {
+ assert (tuple_variations.tuple_vars[0].indices[i] == 1);
+ assert (tuple_variations.tuple_vars[1].indices[i] == 1);
+ assert (roundf (tuple_variations.tuple_vars[0].deltas_x[i]) == rounded_deltas_1[i]);
+ assert (roundf (tuple_variations.tuple_vars[1].deltas_x[i]) == rounded_deltas_2[i]);
+ }
+ }
+
+ hb_map_t axes_index_map;
+ axes_index_map.set (0, 0);
+ bool res = tuple_variations.compile_bytes (axes_index_map, axis_idx_tag_map);
+ assert (res);
+ assert (tuple_variations.tuple_vars[0].compiled_tuple_header.length == 6);
+ const char tuple_var_header_1[] = "\x0\x51\xa0\x0\xc0\x0";
+ for (unsigned i = 0; i < 6; i++)
+ assert(tuple_variations.tuple_vars[0].compiled_tuple_header.arrayZ[i] == tuple_var_header_1[i]);
+
+ assert (tuple_variations.tuple_vars[1].compiled_tuple_header.length == 6);
+ const char tuple_var_header_2[] = "\x0\x54\xa0\x0\x40\x0";
+ for (unsigned i = 0; i < 6; i++)
+ assert(tuple_variations.tuple_vars[1].compiled_tuple_header.arrayZ[i] == tuple_var_header_2[i]);
+
+ assert (tuple_variations.tuple_vars[0].compiled_deltas.length == 37);
+ assert (tuple_variations.tuple_vars[1].compiled_deltas.length == 40);
+ const char compiled_deltas_1[] = "\x0d\xff\x00\xfe\x01\x00\xff\x00\xfe\x01\x00\xed\xed\xf3\xf3\x82\x00\xfe\x84\x06\xfe\x00\x01\xf1\xf1\xf6\xf6\x82\x04\x01\xf1\xf1\xf6\xf6\x82\x00\x01";
+ for (unsigned i = 0; i < 37; i++)
+ assert (tuple_variations.tuple_vars[0].compiled_deltas.arrayZ[i] == compiled_deltas_1[i]);
+
+ const char compiled_deltas_2[] = "\x0d\x01\x00\x04\xfe\x00\x01\x00\x04\xfe\x00\x44\x44\x30\x30\x82\x00\x04\x81\x09\x01\xff\x01\x05\xff\xfc\x33\x33\x25\x25\x82\x04\xff\x33\x33\x25\x25\x82\x00\xff";
+ for (unsigned i = 0; i < 40; i++)
+ assert (tuple_variations.tuple_vars[1].compiled_deltas.arrayZ[i] == compiled_deltas_2[i]);
+}
+
+int
+main (int argc, char **argv)
+{
+ test_decompile_cvar ();
+}
diff --git a/src/test-vector.cc b/src/test-vector.cc
index 65e51c6..cc8ceab 100644
--- a/src/test-vector.cc
+++ b/src/test-vector.cc
@@ -181,6 +181,11 @@
hb_map_t m;
v.push (m);
}
+ {
+ hb_vector_t<hb_map_t> v;
+ hb_map_t m;
+ v.push (m);
+ }
return 0;
}
diff --git a/src/wasm/graphite/Makefile b/src/wasm/graphite/Makefile
new file mode 100644
index 0000000..cdd44dc
--- /dev/null
+++ b/src/wasm/graphite/Makefile
@@ -0,0 +1,28 @@
+FONTS = CharisSIL-R.wasm.ttf Scheherazade-R.wasm.ttf AwamiNastaliq-Regular.wasm.ttf
+ADD_TABLE = ../../addTable.py
+
+all: $(FONTS)
+
+%.wasm: %.cc ../../hb-wasm-api.h
+ emcc \
+ -I ../.. \
+ -I ~/graphite/include/ \
+ -fvisibility=hidden \
+ -Wl,--allow-undefined \
+ -Wl,--no-entry \
+ -Wl,--strip-all \
+ -sERROR_ON_UNDEFINED_SYMBOLS=0 \
+ -Wl,--export=malloc -Wl,--export=free \
+ ~/graphite/src/libgraphite2.a \
+ ~/wasm/wasi-sdk-19.0/share/wasi-sysroot/lib/wasm32-wasi/libc.a \
+ $< \
+ -o $@
+
+
+%.wasm.ttf: %.ttf shape.wasm $(ADD_TABLE)
+ python $(ADD_TABLE) $< $@ shape.wasm
+
+clean:
+ $(RM) shape.wasm $(FONTS)
+
+.PRECIOUS: shape.wasm
diff --git a/src/wasm/graphite/shape.cc b/src/wasm/graphite/shape.cc
new file mode 100644
index 0000000..f445049
--- /dev/null
+++ b/src/wasm/graphite/shape.cc
@@ -0,0 +1,250 @@
+#define HB_WASM_INTERFACE(ret_t, name) __attribute__((export_name(#name))) ret_t name
+
+#include <hb-wasm-api.h>
+
+#include <graphite2/Segment.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+void debugprint1 (char *s, int32_t);
+void debugprint2 (char *s, int32_t, int32_t);
+
+static const void *copy_table (const void *data, unsigned int tag, size_t *len)
+{
+ face_t *face = (face_t *) data;
+ blob_t blob = BLOB_INIT;
+ if (!face_copy_table (face, tag, &blob))
+ abort ();
+
+ *len = blob.length;
+ return blob.data;
+}
+
+static void free_table (const void *data, const void *table_data)
+{
+ blob_t blob;
+ blob.length = 0; // Doesn't matter
+ blob.data = (char *) table_data;
+ blob_free (&blob);
+}
+
+void *
+shape_plan_create (face_t *face)
+{
+ const gr_face_ops ops = {sizeof (gr_face_ops), ©_table, &free_table};
+ gr_face *grface = gr_make_face_with_ops (face, &ops, gr_face_preloadAll);
+ return grface;
+}
+
+void
+shape_plan_destroy (void *data)
+{
+ gr_face_destroy ((gr_face *) data);
+}
+
+bool_t
+shape (void *shape_plan,
+ font_t *font,
+ buffer_t *buffer,
+ const feature_t *features,
+ uint32_t num_features)
+{
+ face_t *face = font_get_face (font);
+ gr_face *grface = (gr_face *) shape_plan;
+
+ direction_t direction = buffer_get_direction (buffer);
+ direction_t horiz_dir = script_get_horizontal_direction (buffer_get_script (buffer));
+ /* TODO vertical:
+ * The only BTT vertical script is Ogham, but it's not clear to me whether OpenType
+ * Ogham fonts are supposed to be implemented BTT or not. Need to research that
+ * first. */
+ if ((DIRECTION_IS_HORIZONTAL (direction) &&
+ direction != horiz_dir && horiz_dir != DIRECTION_INVALID) ||
+ (DIRECTION_IS_VERTICAL (direction) &&
+ direction != DIRECTION_TTB))
+ {
+ buffer_reverse_clusters (buffer);
+ direction = DIRECTION_REVERSE (direction);
+ }
+
+ buffer_contents_t contents = BUFFER_CONTENTS_INIT;
+ if (!buffer_copy_contents (buffer, &contents))
+ return false;
+
+ gr_segment *seg = nullptr;
+ const gr_slot *is;
+ unsigned int ci = 0, ic = 0;
+ unsigned int curradvx = 0, curradvy = 0;
+ unsigned length = contents.length;
+
+ uint32_t *chars = (uint32_t *) malloc (length * sizeof (uint32_t));
+ if (!chars)
+ return false;
+ for (unsigned int i = 0; i < contents.length; ++i)
+ chars[i] = contents.info[i].codepoint;
+
+ seg = gr_make_seg (nullptr, grface,
+ 0, // https://github.com/harfbuzz/harfbuzz/issues/3439#issuecomment-1442650148
+ nullptr,
+ gr_utf32, chars, contents.length,
+ 2 | (direction == DIRECTION_RTL ? 1 : 0));
+
+ free (chars);
+
+ if (!seg)
+ return false;
+
+ unsigned int glyph_count = gr_seg_n_slots (seg);
+
+ struct cluster_t {
+ unsigned int base_char;
+ unsigned int num_chars;
+ unsigned int base_glyph;
+ unsigned int num_glyphs;
+ unsigned int cluster;
+ int advance;
+ };
+
+ length = glyph_count;
+ if (!buffer_contents_realloc (&contents, length))
+ return false;
+ cluster_t *clusters = (cluster_t *) malloc (length * sizeof (cluster_t));
+ uint32_t *gids = (uint32_t *) malloc (length * sizeof (uint32_t));
+ if (!clusters || !gids)
+ {
+ free (clusters);
+ free (gids);
+ return false;
+ }
+
+ memset (clusters, 0, sizeof (clusters[0]) * length);
+ codepoint_t *pg = gids;
+ clusters[0].cluster = contents.info[0].cluster;
+ unsigned int upem = face_get_upem (face);
+ int32_t font_x_scale, font_y_scale;
+ font_get_scale (font, &font_x_scale, &font_y_scale);
+ float xscale = (float) font_x_scale / upem;
+ float yscale = (float) font_y_scale / upem;
+ yscale *= yscale / xscale;
+ unsigned int curradv = 0;
+ if (DIRECTION_IS_BACKWARD (direction))
+ {
+ curradv = gr_slot_origin_X(gr_seg_first_slot(seg)) * xscale;
+ clusters[0].advance = gr_seg_advance_X(seg) * xscale - curradv;
+ }
+ else
+ clusters[0].advance = 0;
+ for (is = gr_seg_first_slot (seg), ic = 0; is; is = gr_slot_next_in_segment (is), ic++)
+ {
+ unsigned int before = gr_slot_before (is);
+ unsigned int after = gr_slot_after (is);
+ *pg = gr_slot_gid (is);
+ pg++;
+ while (clusters[ci].base_char > before && ci)
+ {
+ clusters[ci-1].num_chars += clusters[ci].num_chars;
+ clusters[ci-1].num_glyphs += clusters[ci].num_glyphs;
+ clusters[ci-1].advance += clusters[ci].advance;
+ ci--;
+ }
+
+ if (gr_slot_can_insert_before (is) && clusters[ci].num_chars && before >= clusters[ci].base_char + clusters[ci].num_chars)
+ {
+ cluster_t *c = clusters + ci + 1;
+ c->base_char = clusters[ci].base_char + clusters[ci].num_chars;
+ c->cluster = contents.info[c->base_char].cluster;
+ c->num_chars = before - c->base_char;
+ c->base_glyph = ic;
+ c->num_glyphs = 0;
+ if (DIRECTION_IS_BACKWARD (direction))
+ {
+ c->advance = curradv - gr_slot_origin_X(is) * xscale;
+ curradv -= c->advance;
+ }
+ else
+ {
+ auto origin_X = gr_slot_origin_X (is) * xscale;
+ c->advance = 0;
+ clusters[ci].advance += origin_X - curradv;
+ curradv = origin_X;
+ }
+ ci++;
+ }
+ clusters[ci].num_glyphs++;
+
+ if (clusters[ci].base_char + clusters[ci].num_chars < after + 1)
+ clusters[ci].num_chars = after + 1 - clusters[ci].base_char;
+ }
+
+ if (DIRECTION_IS_BACKWARD (direction))
+ clusters[ci].advance += curradv;
+ else
+ clusters[ci].advance += gr_seg_advance_X(seg) * xscale - curradv;
+ ci++;
+
+ for (unsigned int i = 0; i < ci; ++i)
+ {
+ for (unsigned int j = 0; j < clusters[i].num_glyphs; ++j)
+ {
+ glyph_info_t *info = &contents.info[clusters[i].base_glyph + j];
+ info->codepoint = gids[clusters[i].base_glyph + j];
+ info->cluster = clusters[i].cluster;
+ info->var1 = (unsigned) clusters[i].advance; // all glyphs in the cluster get the same advance
+ }
+ }
+ contents.length = glyph_count;
+
+ /* Positioning. */
+ unsigned int currclus = 0xFFFFFFFF;
+ const glyph_info_t *info = contents.info;
+ glyph_position_t *pPos = contents.pos;
+ if (!DIRECTION_IS_BACKWARD (direction))
+ {
+ curradvx = 0;
+ for (is = gr_seg_first_slot (seg); is; pPos++, ++info, is = gr_slot_next_in_segment (is))
+ {
+ pPos->x_offset = gr_slot_origin_X (is) * xscale - curradvx;
+ pPos->y_offset = gr_slot_origin_Y (is) * yscale - curradvy;
+ if (info->cluster != currclus) {
+ pPos->x_advance = (int) info->var1;
+ curradvx += pPos->x_advance;
+ currclus = info->cluster;
+ } else
+ pPos->x_advance = 0.;
+
+ pPos->y_advance = gr_slot_advance_Y (is, grface, nullptr) * yscale;
+ curradvy += pPos->y_advance;
+ }
+ buffer_set_contents (buffer, &contents);
+ }
+ else
+ {
+ curradvx = gr_seg_advance_X(seg) * xscale;
+ for (is = gr_seg_first_slot (seg); is; pPos++, info++, is = gr_slot_next_in_segment (is))
+ {
+ if (info->cluster != currclus)
+ {
+ pPos->x_advance = (int) info->var1;
+ curradvx -= pPos->x_advance;
+ currclus = info->cluster;
+ } else
+ pPos->x_advance = 0.;
+
+ pPos->y_advance = gr_slot_advance_Y (is, grface, nullptr) * yscale;
+ curradvy -= pPos->y_advance;
+ pPos->x_offset = gr_slot_origin_X (is) * xscale - (int) info->var1 - curradvx + pPos->x_advance;
+ pPos->y_offset = gr_slot_origin_Y (is) * yscale - curradvy;
+ }
+ buffer_set_contents (buffer, &contents);
+ buffer_reverse_clusters (buffer);
+ }
+
+ gr_seg_destroy (seg);
+ free (clusters);
+ free (gids);
+
+ bool ret = glyph_count;
+
+ return ret;
+}
diff --git a/src/wasm/rust/harfbuzz-wasm/Cargo.toml b/src/wasm/rust/harfbuzz-wasm/Cargo.toml
new file mode 100644
index 0000000..fa17de3
--- /dev/null
+++ b/src/wasm/rust/harfbuzz-wasm/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "harfbuzz-wasm"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+kurbo = { version = "0.9.0", optional = true }
diff --git a/src/wasm/rust/harfbuzz-wasm/src/lib.rs b/src/wasm/rust/harfbuzz-wasm/src/lib.rs
new file mode 100644
index 0000000..ea20ce5
--- /dev/null
+++ b/src/wasm/rust/harfbuzz-wasm/src/lib.rs
@@ -0,0 +1,464 @@
+#![warn(missing_docs)]
+#![allow(dead_code)]
+//! Interface to Harfbuzz's WASM exports
+//!
+//! This crate is designed to make it easier to write a
+//! WASM shaper for your font using Rust. It binds the
+//! functions exported by Harfbuzz into the WASM runtime,
+//! and wraps them in an ergonomic interface using Rust
+//! structures. For example, here is a basic shaping engine:
+//!
+//!
+//! ```rust
+//! #[wasm_bindgen]
+//! pub fn shape(font_ref: u32, buf_ref: u32) -> i32 {
+//! let font = Font::from_ref(font_ref);
+//! let mut buffer = GlyphBuffer::from_ref(buf_ref);
+//! for mut item in buffer.glyphs.iter_mut() {
+//! // Map character to glyph
+//! item.codepoint = font.get_glyph(codepoint, 0);
+//! // Set advance width
+//! item.h_advance = font.get_glyph_h_advance(item.codepoint);
+//! }
+//! 1
+//! }
+//! ```
+use std::ffi::{c_int, CStr, CString};
+
+#[cfg(feature = "kurbo")]
+use kurbo::BezPath;
+
+// We don't use #[wasm_bindgen] here because that makes
+// assumptions about Javascript calling conventions. We
+// really do just want to import some C symbols and run
+// them in unsafe-land!
+extern "C" {
+ fn face_get_upem(face: u32) -> u32;
+ fn font_get_face(font: u32) -> u32;
+ fn font_get_glyph(font: u32, unicode: u32, uvs: u32) -> u32;
+ fn font_get_scale(font: u32, x_scale: *mut i32, y_scale: *mut i32);
+ fn font_get_glyph_extents(font: u32, glyph: u32, extents: *mut CGlyphExtents) -> bool;
+ fn font_glyph_to_string(font: u32, glyph: u32, str: *const u8, len: u32);
+ fn font_get_glyph_h_advance(font: u32, glyph: u32) -> i32;
+ fn font_get_glyph_v_advance(font: u32, glyph: u32) -> i32;
+ fn font_copy_glyph_outline(font: u32, glyph: u32, outline: *mut CGlyphOutline) -> bool;
+ fn face_copy_table(font: u32, tag: u32, blob: *mut Blob) -> bool;
+ fn buffer_copy_contents(buffer: u32, cbuffer: *mut CBufferContents) -> bool;
+ fn buffer_set_contents(buffer: u32, cbuffer: &CBufferContents) -> bool;
+ fn debugprint(s: *const u8);
+ fn shape_with(
+ font: u32,
+ buffer: u32,
+ features: u32,
+ num_features: u32,
+ shaper: *const u8,
+ ) -> i32;
+}
+
+/// An opaque reference to a font at a given size and
+/// variation. It is equivalent to the `hb_font_t` pointer
+/// in Harfbuzz.
+#[derive(Debug)]
+pub struct Font(u32);
+
+impl Font {
+ /// Initialize a `Font` struct from the reference provided
+ /// by Harfbuzz to the `shape` function.
+ pub fn from_ref(ptr: u32) -> Self {
+ Self(ptr)
+ }
+ /// Call the given Harfbuzz shaper on a buffer reference.
+ ///
+ /// For example, `font.shape_with(buffer_ref, "ot")` will
+ /// run standard OpenType shaping, allowing you to modify
+ /// the buffer contents after glyph mapping, substitution
+ /// and positioning has taken place.
+ pub fn shape_with(&self, buffer_ref: u32, shaper: &str) {
+ let c_shaper = CString::new(shaper).unwrap();
+ unsafe {
+ shape_with(self.0, buffer_ref, 0, 0, c_shaper.as_ptr() as *const u8);
+ }
+ }
+
+ /// Return the font face object that this font belongs to.
+ pub fn get_face(&self) -> Face {
+ Face(unsafe { font_get_face(self.0) })
+ }
+
+ /// Map a Unicode codepoint to a glyph ID.
+ ///
+ /// The `uvs` parameter specifies a Unicode Variation
+ /// Selector codepoint which is used in conjunction with
+ /// [format 14 cmap tables](https://learn.microsoft.com/en-us/typography/opentype/spec/cmap#format-14-unicode-variation-sequences)
+ /// to provide alternate glyph mappings for characters with
+ /// Unicode Variation Sequences. Generally you will pass
+ /// `0`.
+ pub fn get_glyph(&self, unicode: u32, uvs: u32) -> u32 {
+ unsafe { font_get_glyph(self.0, unicode, uvs) }
+ }
+
+ /// Get the extents for a given glyph ID, in its design position.
+ pub fn get_glyph_extents(&self, glyph: u32) -> CGlyphExtents {
+ let mut extents = CGlyphExtents::default();
+ unsafe {
+ font_get_glyph_extents(self.0, glyph, &mut extents);
+ }
+ extents
+ }
+
+ /// Get the default advance width for a given glyph ID.
+ pub fn get_glyph_h_advance(&self, glyph: u32) -> i32 {
+ unsafe { font_get_glyph_h_advance(self.0, glyph) }
+ }
+
+ /// Get the default vertical advance for a given glyph ID.
+ fn get_glyph_v_advance(&self, glyph: u32) -> i32 {
+ unsafe { font_get_glyph_v_advance(self.0, glyph) }
+ }
+
+ /// Get the name of a glyph.
+ ///
+ /// If no names are provided by the font, names of the form
+ /// `gidXXX` are constructed.
+ pub fn get_glyph_name(&self, glyph: u32) -> String {
+ let mut s = [1u8; 32];
+ unsafe {
+ font_glyph_to_string(self.0, glyph, s.as_mut_ptr(), 32);
+ }
+ unsafe { CStr::from_ptr(s.as_ptr() as *const _) }
+ .to_str()
+ .unwrap()
+ .to_string()
+ }
+
+ /// Get the X and Y scale factor applied to this font.
+ ///
+ /// This should be divided by the units per em value to
+ /// provide a scale factor mapping from design units to
+ /// user units. (See [`Face::get_upem`].)
+ pub fn get_scale(&self) -> (i32, i32) {
+ let mut x_scale: i32 = 0;
+ let mut y_scale: i32 = 0;
+ unsafe {
+ font_get_scale(
+ self.0,
+ &mut x_scale as *mut c_int,
+ &mut y_scale as *mut c_int,
+ )
+ };
+ (x_scale, y_scale)
+ }
+
+ #[cfg(feature = "kurbo")]
+ /// Get the outline of a glyph as a vector of bezier paths
+ pub fn get_outline(&self, glyph: u32) -> Vec<BezPath> {
+ let mut outline = CGlyphOutline {
+ n_points: 0,
+ points: std::ptr::null_mut(),
+ n_contours: 0,
+ contours: std::ptr::null_mut(),
+ };
+ let end_pts_of_contours: &[usize] = unsafe {
+ font_copy_glyph_outline(self.0, glyph, &mut outline);
+ std::slice::from_raw_parts(outline.contours, outline.n_contours as usize)
+ };
+ let points: &[CGlyphOutlinePoint] =
+ unsafe { std::slice::from_raw_parts(outline.points, outline.n_points as usize) };
+ let mut results: Vec<BezPath> = vec![];
+ let mut start_pt: usize = 0;
+ for end_pt in end_pts_of_contours {
+ let this_contour = &points[start_pt..*end_pt];
+ start_pt = *end_pt;
+ let mut path = BezPath::new();
+ let mut ix = 0;
+ while ix < this_contour.len() {
+ let point = &this_contour[ix];
+ match point.pointtype {
+ PointType::MoveTo => path.move_to((point.x as f64, point.y as f64)),
+ PointType::LineTo => path.line_to((point.x as f64, point.y as f64)),
+ PointType::QuadraticTo => {
+ ix += 1;
+ let end_pt = &this_contour[ix];
+ path.quad_to(
+ (point.x as f64, point.y as f64),
+ (end_pt.x as f64, end_pt.y as f64),
+ );
+ }
+ PointType::CubicTo => {
+ ix += 1;
+ let mid_pt = &this_contour[ix];
+ ix += 1;
+ let end_pt = &this_contour[ix];
+ path.curve_to(
+ (point.x as f64, point.y as f64),
+ (mid_pt.x as f64, mid_pt.y as f64),
+ (end_pt.x as f64, end_pt.y as f64),
+ );
+ }
+ }
+ ix += 1;
+ }
+ path.close_path();
+ results.push(path);
+ }
+ results
+ }
+}
+
+/// An opaque reference to a font face, equivalent to the `hb_face_t` pointer
+/// in Harfbuzz.
+///
+/// This is generally returned from [`Font::get_face`].
+#[derive(Debug)]
+pub struct Face(u32);
+
+impl Face {
+ /// Get a blob containing the contents of the given binary font table.
+ pub fn reference_table(&self, tag: &str) -> Blob {
+ let mut tag_u: u32 = 0;
+ let mut chars = tag.chars();
+ tag_u |= (chars.next().unwrap() as u32) << 24;
+ tag_u |= (chars.next().unwrap() as u32) << 16;
+ tag_u |= (chars.next().unwrap() as u32) << 8;
+ tag_u |= chars.next().unwrap() as u32;
+ let mut blob = Blob {
+ data: std::ptr::null_mut(),
+ length: 0,
+ };
+ unsafe {
+ face_copy_table(self.0, tag_u, &mut blob);
+ }
+ blob
+ }
+
+ /// Get the face's design units per em.
+ pub fn get_upem(&self) -> u32 {
+ unsafe { face_get_upem(self.0) }
+ }
+}
+
+/// Trait implemented by custom structs representing buffer items
+pub trait BufferItem {
+ /// Construct an item in your preferred representation out of the info and position data provided by Harfbuzz.
+ fn from_c(info: CGlyphInfo, position: CGlyphPosition) -> Self;
+ /// Return info and position data to Harfbuzz.
+ fn to_c(self) -> (CGlyphInfo, CGlyphPosition);
+}
+
+/// Generic representation of a Harfbuzz buffer item.
+///
+/// By making this generic, we allow you to implement your own
+/// representations of buffer items; for example, in your shaper,
+/// you may want certain fields to keep track of the glyph's name,
+/// extents, or shape, so you would want a custom struct to represent
+/// buffer items. If you don't care about any of them, use the
+/// supplied `GlyphBuffer` struct.
+#[derive(Debug)]
+pub struct Buffer<T: BufferItem> {
+ _ptr: u32,
+ /// Glyphs in the buffer
+ pub glyphs: Vec<T>,
+}
+
+impl<T: BufferItem> Buffer<T> {
+ /// Construct a buffer from the pointer Harfbuzz provides to the WASM.
+ ///
+ /// The `Buffer` struct implements Drop, meaning that when the shaping
+ /// function is finished, the buffer contents are sent back to Harfbuzz.
+ pub fn from_ref(ptr: u32) -> Self {
+ let mut c_contents = CBufferContents {
+ info: std::ptr::null_mut(),
+ position: std::ptr::null_mut(),
+ length: 0,
+ };
+
+ unsafe {
+ buffer_copy_contents(ptr, &mut c_contents) || panic!("Couldn't copy buffer contents")
+ };
+ let positions: Vec<CGlyphPosition> = unsafe {
+ std::slice::from_raw_parts(c_contents.position, c_contents.length as usize).to_vec()
+ };
+ let infos: Vec<CGlyphInfo> = unsafe {
+ std::slice::from_raw_parts(c_contents.info, c_contents.length as usize).to_vec()
+ };
+ Buffer {
+ glyphs: infos
+ .into_iter()
+ .zip(positions.into_iter())
+ .map(|(i, p)| T::from_c(i, p))
+ .collect(),
+ _ptr: ptr,
+ }
+ }
+}
+
+impl<T: BufferItem> Drop for Buffer<T> {
+ fn drop(&mut self) {
+ let mut positions: Vec<CGlyphPosition>;
+ let mut infos: Vec<CGlyphInfo>;
+ let glyphs = std::mem::take(&mut self.glyphs);
+ (infos, positions) = glyphs.into_iter().map(|g| g.to_c()).unzip();
+ let c_contents = CBufferContents {
+ length: positions.len() as u32,
+ info: infos[..].as_mut_ptr(),
+ position: positions[..].as_mut_ptr(),
+ };
+ unsafe {
+ if !buffer_set_contents(self._ptr, &c_contents) {
+ panic!("Couldn't set buffer contents");
+ }
+ }
+ }
+}
+
+/// Some data provided by Harfbuzz.
+#[derive(Debug)]
+#[repr(C)]
+pub struct Blob {
+ /// Length of the blob in bytes
+ pub length: u32,
+ /// A raw pointer to the contents
+ pub data: *mut u8,
+}
+
+/// Glyph information in a buffer item provided by Harfbuzz
+///
+/// You'll only need to interact with this if you're writing
+/// your own buffer item structure.
+#[repr(C)]
+#[derive(Debug, Clone)]
+pub struct CGlyphInfo {
+ pub codepoint: u32,
+ pub mask: u32,
+ pub cluster: u32,
+ pub var1: u32,
+ pub var2: u32,
+}
+
+/// Glyph positioning information in a buffer item provided by Harfbuzz
+///
+/// You'll only need to interact with this if you're writing
+/// your own buffer item structure.
+#[derive(Debug, Clone)]
+#[repr(C)]
+pub struct CGlyphPosition {
+ pub x_advance: i32,
+ pub y_advance: i32,
+ pub x_offset: i32,
+ pub y_offset: i32,
+ pub var: u32,
+}
+
+/// Glyph extents
+#[derive(Debug, Clone, Default)]
+#[repr(C)]
+pub struct CGlyphExtents {
+ /// The scaled left side bearing of the glyph
+ pub x_bearing: i32,
+ /// The scaled coordinate of the top of the glyph
+ pub y_bearing: i32,
+ /// The width of the glyph
+ pub width: i32,
+ /// The height of the glyph
+ pub height: i32,
+}
+
+#[derive(Debug)]
+#[repr(C)]
+struct CBufferContents {
+ length: u32,
+ info: *mut CGlyphInfo,
+ position: *mut CGlyphPosition,
+}
+
+/// Ergonomic representation of a Harfbuzz buffer item
+///
+/// Harfbuzz buffers are normally split into two arrays,
+/// one representing glyph information and the other
+/// representing glyph positioning. In Rust, this would
+/// require lots of zipping and unzipping, so we zip them
+/// together into a single structure for you.
+#[derive(Debug, Clone, Copy)]
+pub struct Glyph {
+ /// The Unicode codepoint or glyph ID of the item
+ pub codepoint: u32,
+ /// The index of the cluster in the input text where this came from
+ pub cluster: u32,
+ /// The horizontal advance of the glyph
+ pub x_advance: i32,
+ /// The vertical advance of the glyph
+ pub y_advance: i32,
+ /// The horizontal offset of the glyph
+ pub x_offset: i32,
+ /// The vertical offset of the glyph
+ pub y_offset: i32,
+ /// You can use this for whatever you like
+ pub flags: u32,
+}
+impl BufferItem for Glyph {
+ fn from_c(info: CGlyphInfo, pos: CGlyphPosition) -> Self {
+ Self {
+ codepoint: info.codepoint,
+ cluster: info.cluster,
+ x_advance: pos.x_advance,
+ y_advance: pos.y_advance,
+ x_offset: pos.x_offset,
+ y_offset: pos.y_offset,
+ flags: 0,
+ }
+ }
+ fn to_c(self) -> (CGlyphInfo, CGlyphPosition) {
+ let info = CGlyphInfo {
+ codepoint: self.codepoint,
+ cluster: self.cluster,
+ mask: 0,
+ var1: 0,
+ var2: 0,
+ };
+ let pos = CGlyphPosition {
+ x_advance: self.x_advance,
+ y_advance: self.y_advance,
+ x_offset: self.x_offset,
+ y_offset: self.y_offset,
+ var: 0,
+ };
+ (info, pos)
+ }
+}
+
+#[repr(C)]
+#[allow(clippy::enum_variant_names)]
+#[derive(Clone, Debug)]
+enum PointType {
+ MoveTo,
+ LineTo,
+ QuadraticTo,
+ CubicTo,
+}
+
+#[repr(C)]
+#[derive(Clone, Debug)]
+struct CGlyphOutlinePoint {
+ x: f32,
+ y: f32,
+ pointtype: PointType,
+}
+
+#[repr(C)]
+struct CGlyphOutline {
+ n_points: usize,
+ points: *mut CGlyphOutlinePoint,
+ n_contours: usize,
+ contours: *mut usize,
+}
+
+/// Our default buffer item struct. See also [`Glyph`].
+pub type GlyphBuffer = Buffer<Glyph>;
+
+/// Write a string to the Harfbuzz debug log.
+pub fn debug(s: &str) {
+ let c_s = CString::new(s).unwrap();
+ unsafe {
+ debugprint(c_s.as_ptr() as *const u8);
+ };
+}
diff --git a/src/wasm/sample/c/Makefile b/src/wasm/sample/c/Makefile
new file mode 100644
index 0000000..4ee073b
--- /dev/null
+++ b/src/wasm/sample/c/Makefile
@@ -0,0 +1,25 @@
+ADD_TABLE = ../../../addTable.py
+
+all: test-fallback.wasm.ttf test-ot.wasm.ttf
+
+%.wasm: %.cc ../../../hb-wasm-api.h
+ clang \
+ --target=wasm32-unknown-wasi \
+ -Wl,--no-entry \
+ -fvisibility=hidden \
+ -Wl,--allow-undefined \
+ -nostdlib \
+ -I ../../.. \
+ $< \
+ -o $@
+
+test-fallback.wasm.ttf: test.ttf shape-fallback.wasm $(ADD_TABLE)
+ python $(ADD_TABLE) $< $@ shape-fallback.wasm
+
+test-ot.wasm.ttf: test.ttf shape-ot.wasm $(ADD_TABLE)
+ python $(ADD_TABLE) $< $@ shape-ot.wasm
+
+clean:
+ $(RM) test-fallback.wasm.ttf test-ot.wasm.ttf shape-fallback.wasm shape-ot.wasm
+
+.PRECIOUS: *.wasm
diff --git a/src/wasm/sample/c/shape-fallback.cc b/src/wasm/sample/c/shape-fallback.cc
new file mode 100644
index 0000000..7787bba
--- /dev/null
+++ b/src/wasm/sample/c/shape-fallback.cc
@@ -0,0 +1,60 @@
+#define HB_WASM_INTERFACE(ret_t, name) __attribute__((export_name(#name))) ret_t name
+
+#include <hb-wasm-api.h>
+
+extern "C" {
+void debugprint (const char *s);
+void debugprint1 (const char *s, int32_t);
+void debugprint2 (const char *s, int32_t, int32_t);
+}
+
+bool_t
+shape (void *shape_plan,
+ font_t *font,
+ buffer_t *buffer,
+ const feature_t *features,
+ uint32_t num_features)
+{
+ face_t *face = font_get_face (font);
+
+ blob_t blob = BLOB_INIT;
+ face_copy_table (face, TAG ('c','m','a','p'), &blob);
+
+ debugprint1 ("cmap length", blob.length);
+
+ blob_free (&blob);
+
+ buffer_contents_t contents = BUFFER_CONTENTS_INIT;
+ if (!buffer_copy_contents (buffer, &contents))
+ return false;
+
+ debugprint1 ("buffer length", contents.length);
+
+ glyph_outline_t outline = GLYPH_OUTLINE_INIT;
+
+ for (unsigned i = 0; i < contents.length; i++)
+ {
+ char name[64];
+
+ debugprint1 ("glyph at", i);
+
+ font_glyph_to_string (font, contents.info[i].codepoint, name, sizeof (name));
+
+ debugprint (name);
+
+ contents.info[i].codepoint = font_get_glyph (font, contents.info[i].codepoint, 0);
+ contents.pos[i].x_advance = font_get_glyph_h_advance (font, contents.info[i].codepoint);
+
+ font_copy_glyph_outline (font, contents.info[i].codepoint, &outline);
+ debugprint1 ("num outline points", outline.n_points);
+ debugprint1 ("num outline contours", outline.n_contours);
+ }
+
+ glyph_outline_free (&outline);
+
+ bool_t ret = buffer_set_contents (buffer, &contents);
+
+ buffer_contents_free (&contents);
+
+ return ret;
+}
diff --git a/src/wasm/sample/c/shape-ot.cc b/src/wasm/sample/c/shape-ot.cc
new file mode 100644
index 0000000..9081cfe
--- /dev/null
+++ b/src/wasm/sample/c/shape-ot.cc
@@ -0,0 +1,18 @@
+#define HB_WASM_INTERFACE(ret_t, name) __attribute__((export_name(#name))) ret_t name
+
+#include <hb-wasm-api.h>
+
+extern "C" {
+void debugprint1 (const char *s, int32_t);
+void debugprint2 (const char *s, int32_t, int32_t);
+}
+
+bool_t
+shape (void *shape_plan,
+ font_t *font,
+ buffer_t *buffer,
+ const feature_t *features,
+ uint32_t num_features)
+{
+ return shape_with (font, buffer, features, num_features, "ot");
+}
diff --git a/src/wasm/sample/c/test.ttf b/src/wasm/sample/c/test.ttf
new file mode 100644
index 0000000..2ba04f6
--- /dev/null
+++ b/src/wasm/sample/c/test.ttf
Binary files differ
diff --git a/src/wasm/sample/rust/hello-wasm/Cargo.toml b/src/wasm/sample/rust/hello-wasm/Cargo.toml
new file mode 100644
index 0000000..53357ea
--- /dev/null
+++ b/src/wasm/sample/rust/hello-wasm/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "hello-wasm"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+crate-type = ["cdylib"]
+
+[dependencies]
+#externref = "0.1.0"
+wasm-bindgen = "0.2.0"
+tiny-rng = "0.2.0"
+harfbuzz-wasm = { path="../../../rust/harfbuzz-wasm"}
diff --git a/src/wasm/sample/rust/hello-wasm/src/lib.rs b/src/wasm/sample/rust/hello-wasm/src/lib.rs
new file mode 100644
index 0000000..ab8f153
--- /dev/null
+++ b/src/wasm/sample/rust/hello-wasm/src/lib.rs
@@ -0,0 +1,24 @@
+use harfbuzz_wasm::{Font, GlyphBuffer};
+use tiny_rng::{Rand, Rng};
+use wasm_bindgen::prelude::*;
+
+#[wasm_bindgen]
+pub fn shape(
+ _shape_plan: u32,
+ font_ref: u32,
+ buf_ref: u32,
+ _features: u32,
+ _num_features: u32,
+) -> i32 {
+ let mut rng = Rng::from_seed(123456);
+ let font = Font::from_ref(font_ref);
+ font.shape_with(buf_ref, "ot");
+ let mut buffer = GlyphBuffer::from_ref(buf_ref);
+ for mut item in buffer.glyphs.iter_mut() {
+ // Randomize it!
+ item.x_offset = ((rng.rand_u32() as i32) >> 24) - 120;
+ item.y_offset = ((rng.rand_u32() as i32) >> 24) - 120;
+ }
+ // Buffer is written back to HB on drop
+ 1
+}
diff --git a/test/api/Makefile.am b/test/api/Makefile.am
index 950b738..efeecb4 100644
--- a/test/api/Makefile.am
+++ b/test/api/Makefile.am
@@ -33,6 +33,7 @@
TEST_PROGS = \
test-aat-layout \
test-baseline \
+ test-base-minmax \
test-be-glyph-advance \
test-be-num-glyphs \
test-blob \
diff --git a/test/api/fonts/AdobeVFPrototype.ac.retaingids.otf b/test/api/fonts/AdobeVFPrototype.ac.retaingids.otf
index 8ff99c6..50b5be6 100644
--- a/test/api/fonts/AdobeVFPrototype.ac.retaingids.otf
+++ b/test/api/fonts/AdobeVFPrototype.ac.retaingids.otf
Binary files differ
diff --git a/test/api/fonts/SourceSansVariable-Roman.abc.ttf b/test/api/fonts/SourceSansVariable-Roman.abc.ttf
index dbbc388..01fb89e 100644
--- a/test/api/fonts/SourceSansVariable-Roman.abc.ttf
+++ b/test/api/fonts/SourceSansVariable-Roman.abc.ttf
Binary files differ
diff --git a/test/api/fonts/SourceSansVariable-Roman.ac.retaingids.ttf b/test/api/fonts/SourceSansVariable-Roman.ac.retaingids.ttf
index 9f9ecd6..50b5be6 100644
--- a/test/api/fonts/SourceSansVariable-Roman.ac.retaingids.ttf
+++ b/test/api/fonts/SourceSansVariable-Roman.ac.retaingids.ttf
Binary files differ
diff --git a/test/api/fonts/SourceSansVariable-Roman.ac.ttf b/test/api/fonts/SourceSansVariable-Roman.ac.ttf
index c12daf2..28ca0bd 100644
--- a/test/api/fonts/SourceSansVariable-Roman.ac.ttf
+++ b/test/api/fonts/SourceSansVariable-Roman.ac.ttf
Binary files differ
diff --git a/test/api/fonts/base-minmax.ttf b/test/api/fonts/base-minmax.ttf
new file mode 100644
index 0000000..9661b59
--- /dev/null
+++ b/test/api/fonts/base-minmax.ttf
Binary files differ
diff --git a/test/api/meson.build b/test/api/meson.build
index ab2ab1e..64d7797 100644
--- a/test/api/meson.build
+++ b/test/api/meson.build
@@ -6,6 +6,7 @@
tests = [
'test-aat-layout.c',
'test-baseline.c',
+ 'test-base-minmax.c',
'test-be-glyph-advance.c',
'test-be-num-glyphs.c',
'test-blob.c',
diff --git a/test/api/test-base-minmax.c b/test/api/test-base-minmax.c
new file mode 100644
index 0000000..332c266
--- /dev/null
+++ b/test/api/test-base-minmax.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright © 2023 Behdad Esfahbod
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#include "hb-test.h"
+
+#include <hb-ot.h>
+
+/* Unit tests for hb-ot-layout.h font extents */
+
+static void
+test_ot_layout_font_extents (void)
+{
+ hb_face_t *face = hb_test_open_font_file ("fonts/base-minmax.ttf");
+ hb_font_t *font = hb_font_create (face);
+
+ hb_font_extents_t extents;
+
+ g_assert (hb_ot_layout_get_font_extents2 (font, HB_DIRECTION_LTR,
+ HB_SCRIPT_LATIN, HB_LANGUAGE_INVALID,
+ &extents));
+ g_assert_cmpint (extents.ascender, ==, 2000);
+
+ g_assert (hb_ot_layout_get_font_extents2 (font, HB_DIRECTION_LTR,
+ HB_SCRIPT_LATIN, hb_language_from_string ("xx", -1),
+ &extents));
+ g_assert_cmpint (extents.ascender, ==, 2000);
+
+ g_assert (!hb_ot_layout_get_font_extents2 (font, HB_DIRECTION_LTR,
+ HB_SCRIPT_ARABIC, HB_LANGUAGE_INVALID,
+ &extents));
+ g_assert_cmpint (extents.ascender, ==, 3000);
+
+ hb_font_destroy (font);
+ hb_face_destroy (face);
+}
+
+int
+main (int argc, char **argv)
+{
+ hb_test_init (&argc, &argv);
+
+ hb_test_add (test_ot_layout_font_extents);
+
+ return hb_test_run();
+}
diff --git a/test/api/test-baseline.c b/test/api/test-baseline.c
index 9f9542e..4e02cd0 100644
--- a/test/api/test-baseline.c
+++ b/test/api/test-baseline.c
@@ -41,6 +41,12 @@
&position));
g_assert_cmpint (46, ==, position);
+ g_assert (hb_ot_layout_get_baseline2 (font, HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_BOTTOM_OR_LEFT, HB_DIRECTION_TTB,
+ HB_SCRIPT_HAN,
+ hb_language_from_string ("en", -1),
+ &position));
+ g_assert_cmpint (46, ==, position);
+
g_assert (!hb_ot_layout_get_baseline (font, HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_TOP_OR_RIGHT, HB_DIRECTION_TTB,
HB_TAG ('h','a','n','i'),
HB_TAG ('E','N','G',' '),
@@ -63,6 +69,12 @@
&position);
g_assert_cmpint (46, ==, position);
+ hb_ot_layout_get_baseline_with_fallback2 (font, HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_BOTTOM_OR_LEFT, HB_DIRECTION_TTB,
+ HB_SCRIPT_HAN,
+ hb_language_from_string ("en", -1),
+ &position);
+ g_assert_cmpint (46, ==, position);
+
hb_ot_layout_get_baseline_with_fallback (font, HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_TOP_OR_RIGHT, HB_DIRECTION_TTB,
HB_TAG ('h','a','n','i'),
HB_TAG ('E','N','G',' '),
diff --git a/test/api/test-ot-face.c b/test/api/test-ot-face.c
index 7ed96aa..ba3cfd3 100644
--- a/test/api/test-ot-face.c
+++ b/test/api/test-ot-face.c
@@ -188,7 +188,7 @@
hb_ot_var_normalize_coords (face, 0, NULL, NULL);
hb_draw_funcs_t *funcs = hb_draw_funcs_create ();
- hb_font_get_glyph_shape (font, cp, funcs, NULL);
+ hb_font_draw_glyph (font, cp, funcs, NULL);
hb_draw_funcs_destroy (funcs);
hb_set_destroy (set);
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-hb-subset-fuzzer-5979721620652032 b/test/fuzzing/fonts/clusterfuzz-testcase-hb-subset-fuzzer-5979721620652032
new file mode 100644
index 0000000..6e54cba
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-hb-subset-fuzzer-5979721620652032
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-draw-fuzzer-5692635449524224 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-draw-fuzzer-5692635449524224
new file mode 100644
index 0000000..14cdfde
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-draw-fuzzer-5692635449524224
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-repacker-fuzzer-4877513265119232 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-repacker-fuzzer-4877513265119232
new file mode 100644
index 0000000..e91513d
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-repacker-fuzzer-4877513265119232
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-4787105656864768 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-4787105656864768
new file mode 100644
index 0000000..beb4031
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-4787105656864768
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5114131137822720 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5114131137822720
new file mode 100644
index 0000000..7a37e98
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-shape-fuzzer-5114131137822720
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-4552226966994944 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-4552226966994944
new file mode 100644
index 0000000..246e4c9
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-4552226966994944
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-4916785942757376 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-4916785942757376
new file mode 100644
index 0000000..15b49ce
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-4916785942757376
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5029952234586112 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5029952234586112
new file mode 100644
index 0000000..39d6514
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5029952234586112
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5191907895279616 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5191907895279616
new file mode 100644
index 0000000..a1936d0
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5191907895279616
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5419002026131456 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5419002026131456
new file mode 100644
index 0000000..5f7fd63
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5419002026131456
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5855710991482880.fuzz b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5855710991482880.fuzz
new file mode 100644
index 0000000..93c5adc
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5855710991482880.fuzz
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-6187272924692480 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-6187272924692480
new file mode 100644
index 0000000..db9f11b
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-6187272924692480
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-6292420615340032 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-6292420615340032
new file mode 100644
index 0000000..62cfbb2
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-6292420615340032
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-6442117271257088 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-6442117271257088
new file mode 100644
index 0000000..8d6284c
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-6442117271257088
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-6608005089853440 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-6608005089853440
new file mode 100644
index 0000000..6702919
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-6608005089853440
Binary files differ
diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-6681253479579648 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-6681253479579648
new file mode 100644
index 0000000..b6239d6
--- /dev/null
+++ b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-6681253479579648
Binary files differ
diff --git a/test/fuzzing/graphs/clusterfuzz-testcase-minimized-hb-repacker-fuzzer-6419865171525632 b/test/fuzzing/graphs/clusterfuzz-testcase-minimized-hb-repacker-fuzzer-6419865171525632
new file mode 100644
index 0000000..2deb99f
--- /dev/null
+++ b/test/fuzzing/graphs/clusterfuzz-testcase-minimized-hb-repacker-fuzzer-6419865171525632
Binary files differ
diff --git a/test/shape/data/in-house/fonts/d0430ea499348c420946f6abc2efc84fdf8f00e3.ttf b/test/shape/data/in-house/fonts/d0430ea499348c420946f6abc2efc84fdf8f00e3.ttf
new file mode 100644
index 0000000..5ecda48
--- /dev/null
+++ b/test/shape/data/in-house/fonts/d0430ea499348c420946f6abc2efc84fdf8f00e3.ttf
Binary files differ
diff --git a/test/shape/data/in-house/tests/use-syllable.tests b/test/shape/data/in-house/tests/use-syllable.tests
index e7cec5f..8e5bd3d 100644
--- a/test/shape/data/in-house/tests/use-syllable.tests
+++ b/test/shape/data/in-house/tests/use-syllable.tests
@@ -22,3 +22,4 @@
../fonts/2a670df15b73a5dc75a5cc491bde5ac93c5077dc.ttf;;U+11124,U+200D,U+11127;[u11124=0+514|u11127=0+0]
../fonts/2a670df15b73a5dc75a5cc491bde5ac93c5077dc.ttf;;U+11124,U+2060,U+11127;[u11124=0+514|uni25CC=1+547|u11127=1+0]
../fonts/a56745bac8449d0ad94918b2bb5930716ba02fe3.ttf;;U+1142C,U+11442,U+200C,U+1142E;[u1142C=0+547|u11442=0+0|u1142E=3+547]
+../fonts/d0430ea499348c420946f6abc2efc84fdf8f00e3.ttf;;U+1142C,U+11442,U+1140E,U+1145E;[u1140E=0+736|u1142C_u11442=0+0|u1145E=0+0]