umwd-components 0.1.827 → 0.1.829
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/node_modules/object-assign/index.js +2 -2
- package/dist/cjs/node_modules/prop-types/factoryWithTypeCheckers.js +1 -1
- package/dist/cjs/node_modules/prop-types/index.js +1 -1
- package/dist/cjs/node_modules/prop-types/node_modules/react-is/index.js +1 -1
- package/dist/cjs/node_modules/safe-buffer/index.js +1 -1
- package/dist/cjs/src/components/e-commerce/products/EditReturnStockForm.js +1 -1
- package/dist/cjs/src/components/logistics/report/ReportMakingComponent.js +1 -1
- package/dist/cjs/src/components/logistics/vendor/EditVendorForm.js +1 -1
- package/dist/cjs/src/data/actions/e-commerce/lead/contactFormAction.js +7 -0
- package/dist/cjs/src/data/actions/logistics/note/createNoteAction.js +1 -1
- package/dist/cjs/src/data/actions/logistics/report/createReportAction.js +1 -1
- package/dist/cjs/src/index.js +1 -1
- package/dist/cjs/tsconfig.build.tsbuildinfo +1 -1
- package/dist/esm/_virtual/index2.js +2 -2
- package/dist/esm/_virtual/index3.js +2 -2
- package/dist/esm/_virtual/index4.js +2 -2
- package/dist/esm/node_modules/object-assign/index.js +74 -69
- package/dist/esm/node_modules/prop-types/factoryWithTypeCheckers.js +2 -2
- package/dist/esm/node_modules/prop-types/index.js +1 -1
- package/dist/esm/node_modules/prop-types/node_modules/react-is/index.js +1 -1
- package/dist/esm/node_modules/safe-buffer/index.js +1 -1
- package/dist/esm/src/components/e-commerce/products/EditReturnStockForm.js +11 -22
- package/dist/esm/src/components/logistics/report/ReportMakingComponent.js +0 -1
- package/dist/esm/src/components/logistics/vendor/EditVendorForm.js +0 -1
- package/dist/esm/src/data/actions/e-commerce/lead/contactFormAction.js +42 -0
- package/dist/esm/src/data/actions/logistics/note/createNoteAction.js +0 -2
- package/dist/esm/src/data/actions/logistics/report/createReportAction.js +0 -2
- package/dist/esm/src/index.js +1 -1
- package/dist/esm/tsconfig.build.tsbuildinfo +1 -1
- package/dist/esm/types/data/actions/e-commerce/lead/contactFormAction.d.ts +1 -0
- package/dist/esm/types/index.d.ts +1 -3
- package/package.json +1 -1
- package/dist/cjs/_virtual/_commonjs-dynamic-modules.js +0 -6
- package/dist/cjs/_virtual/aes.js +0 -6
- package/dist/cjs/_virtual/b64.js +0 -6
- package/dist/cjs/_virtual/blowfish.js +0 -6
- package/dist/cjs/_virtual/cipher-core.js +0 -6
- package/dist/cjs/_virtual/clone.js +0 -6
- package/dist/cjs/_virtual/common.js +0 -6
- package/dist/cjs/_virtual/context.js +0 -6
- package/dist/cjs/_virtual/core2.js +0 -6
- package/dist/cjs/_virtual/decode.js +0 -6
- package/dist/cjs/_virtual/deflate.js +0 -6
- package/dist/cjs/_virtual/dictionary-browser.js +0 -6
- package/dist/cjs/_virtual/dictionary.js +0 -6
- package/dist/cjs/_virtual/en-us.js +0 -6
- package/dist/cjs/_virtual/enc-base64.js +0 -6
- package/dist/cjs/_virtual/enc-base64url.js +0 -6
- package/dist/cjs/_virtual/enc-utf16.js +0 -6
- package/dist/cjs/_virtual/events.js +0 -6
- package/dist/cjs/_virtual/evpkdf.js +0 -6
- package/dist/cjs/_virtual/format-hex.js +0 -6
- package/dist/cjs/_virtual/hmac.js +0 -6
- package/dist/cjs/_virtual/huffman.js +0 -6
- package/dist/cjs/_virtual/hyphen.js +0 -6
- package/dist/cjs/_virtual/index10.js +0 -6
- package/dist/cjs/_virtual/index5.js +0 -6
- package/dist/cjs/_virtual/index6.js +0 -6
- package/dist/cjs/_virtual/index7.js +0 -6
- package/dist/cjs/_virtual/index8.js +0 -6
- package/dist/cjs/_virtual/index9.js +0 -6
- package/dist/cjs/_virtual/inflate.js +0 -6
- package/dist/cjs/_virtual/inherits_browser.js +0 -6
- package/dist/cjs/_virtual/lib-typedarrays.js +0 -6
- package/dist/cjs/_virtual/md5.js +0 -6
- package/dist/cjs/_virtual/mode-cfb.js +0 -6
- package/dist/cjs/_virtual/mode-ctr-gladman.js +0 -6
- package/dist/cjs/_virtual/mode-ctr.js +0 -6
- package/dist/cjs/_virtual/mode-ecb.js +0 -6
- package/dist/cjs/_virtual/mode-ofb.js +0 -6
- package/dist/cjs/_virtual/pad-ansix923.js +0 -6
- package/dist/cjs/_virtual/pad-iso10126.js +0 -6
- package/dist/cjs/_virtual/pad-iso97971.js +0 -6
- package/dist/cjs/_virtual/pad-nopadding.js +0 -6
- package/dist/cjs/_virtual/pad-zeropadding.js +0 -6
- package/dist/cjs/_virtual/pbkdf2.js +0 -6
- package/dist/cjs/_virtual/prefix.js +0 -6
- package/dist/cjs/_virtual/rabbit-legacy.js +0 -6
- package/dist/cjs/_virtual/rabbit.js +0 -6
- package/dist/cjs/_virtual/rc4.js +0 -6
- package/dist/cjs/_virtual/ripemd160.js +0 -6
- package/dist/cjs/_virtual/scheduler.development.js +0 -6
- package/dist/cjs/_virtual/scheduler.production.js +0 -6
- package/dist/cjs/_virtual/sha1.js +0 -6
- package/dist/cjs/_virtual/sha224.js +0 -6
- package/dist/cjs/_virtual/sha256.js +0 -6
- package/dist/cjs/_virtual/sha3.js +0 -6
- package/dist/cjs/_virtual/sha384.js +0 -6
- package/dist/cjs/_virtual/sha512.js +0 -6
- package/dist/cjs/_virtual/streams.js +0 -6
- package/dist/cjs/_virtual/transform.js +0 -6
- package/dist/cjs/_virtual/trees.js +0 -6
- package/dist/cjs/_virtual/tripledes.js +0 -6
- package/dist/cjs/_virtual/x64-core.js +0 -6
- package/dist/cjs/node_modules/@react-pdf/fns/lib/index.js +0 -6
- package/dist/cjs/node_modules/@react-pdf/font/lib/index.browser.js +0 -6
- package/dist/cjs/node_modules/@react-pdf/image/lib/index.browser.js +0 -6
- package/dist/cjs/node_modules/@react-pdf/layout/lib/index.js +0 -6
- package/dist/cjs/node_modules/@react-pdf/pdfkit/lib/pdfkit.browser.js +0 -6
- package/dist/cjs/node_modules/@react-pdf/png-js/lib/png-js.browser.js +0 -6
- package/dist/cjs/node_modules/@react-pdf/primitives/lib/index.js +0 -6
- package/dist/cjs/node_modules/@react-pdf/reconciler/lib/index.js +0 -6
- package/dist/cjs/node_modules/@react-pdf/reconciler/lib/reconciler-23.js +0 -14
- package/dist/cjs/node_modules/@react-pdf/reconciler/lib/reconciler-31.js +0 -26
- package/dist/cjs/node_modules/@react-pdf/render/lib/index.js +0 -6
- package/dist/cjs/node_modules/@react-pdf/renderer/lib/react-pdf.browser.js +0 -6
- package/dist/cjs/node_modules/@react-pdf/stylesheet/lib/index.js +0 -6
- package/dist/cjs/node_modules/@react-pdf/textkit/lib/textkit.js +0 -13
- package/dist/cjs/node_modules/@swc/helpers/esm/_define_property.js +0 -6
- package/dist/cjs/node_modules/abs-svg-path/index.js +0 -6
- package/dist/cjs/node_modules/base64-js/index.js +0 -6
- package/dist/cjs/node_modules/bidi-js/dist/bidi.js +0 -6
- package/dist/cjs/node_modules/brotli/dec/bit_reader.js +0 -6
- package/dist/cjs/node_modules/brotli/dec/context.js +0 -6
- package/dist/cjs/node_modules/brotli/dec/decode.js +0 -6
- package/dist/cjs/node_modules/brotli/dec/dictionary-browser.js +0 -6
- package/dist/cjs/node_modules/brotli/dec/dictionary.bin.js +0 -6
- package/dist/cjs/node_modules/brotli/dec/dictionary.js +0 -6
- package/dist/cjs/node_modules/brotli/dec/huffman.js +0 -6
- package/dist/cjs/node_modules/brotli/dec/prefix.js +0 -6
- package/dist/cjs/node_modules/brotli/dec/streams.js +0 -6
- package/dist/cjs/node_modules/brotli/dec/transform.js +0 -6
- package/dist/cjs/node_modules/brotli/decompress.js +0 -6
- package/dist/cjs/node_modules/clone/clone.js +0 -6
- package/dist/cjs/node_modules/color-name/index.js +0 -6
- package/dist/cjs/node_modules/color-string/index.js +0 -6
- package/dist/cjs/node_modules/crypto-js/aes.js +0 -6
- package/dist/cjs/node_modules/crypto-js/blowfish.js +0 -6
- package/dist/cjs/node_modules/crypto-js/cipher-core.js +0 -6
- package/dist/cjs/node_modules/crypto-js/core.js +0 -6
- package/dist/cjs/node_modules/crypto-js/enc-base64.js +0 -6
- package/dist/cjs/node_modules/crypto-js/enc-base64url.js +0 -6
- package/dist/cjs/node_modules/crypto-js/enc-utf16.js +0 -6
- package/dist/cjs/node_modules/crypto-js/evpkdf.js +0 -6
- package/dist/cjs/node_modules/crypto-js/format-hex.js +0 -6
- package/dist/cjs/node_modules/crypto-js/hmac.js +0 -6
- package/dist/cjs/node_modules/crypto-js/index.js +0 -6
- package/dist/cjs/node_modules/crypto-js/lib-typedarrays.js +0 -6
- package/dist/cjs/node_modules/crypto-js/md5.js +0 -6
- package/dist/cjs/node_modules/crypto-js/mode-cfb.js +0 -6
- package/dist/cjs/node_modules/crypto-js/mode-ctr-gladman.js +0 -12
- package/dist/cjs/node_modules/crypto-js/mode-ctr.js +0 -6
- package/dist/cjs/node_modules/crypto-js/mode-ecb.js +0 -6
- package/dist/cjs/node_modules/crypto-js/mode-ofb.js +0 -6
- package/dist/cjs/node_modules/crypto-js/pad-ansix923.js +0 -6
- package/dist/cjs/node_modules/crypto-js/pad-iso10126.js +0 -6
- package/dist/cjs/node_modules/crypto-js/pad-iso97971.js +0 -6
- package/dist/cjs/node_modules/crypto-js/pad-nopadding.js +0 -6
- package/dist/cjs/node_modules/crypto-js/pad-zeropadding.js +0 -6
- package/dist/cjs/node_modules/crypto-js/pbkdf2.js +0 -6
- package/dist/cjs/node_modules/crypto-js/rabbit-legacy.js +0 -6
- package/dist/cjs/node_modules/crypto-js/rabbit.js +0 -6
- package/dist/cjs/node_modules/crypto-js/rc4.js +0 -6
- package/dist/cjs/node_modules/crypto-js/ripemd160.js +0 -17
- package/dist/cjs/node_modules/crypto-js/sha1.js +0 -6
- package/dist/cjs/node_modules/crypto-js/sha224.js +0 -6
- package/dist/cjs/node_modules/crypto-js/sha256.js +0 -6
- package/dist/cjs/node_modules/crypto-js/sha3.js +0 -6
- package/dist/cjs/node_modules/crypto-js/sha384.js +0 -6
- package/dist/cjs/node_modules/crypto-js/sha512.js +0 -6
- package/dist/cjs/node_modules/crypto-js/tripledes.js +0 -6
- package/dist/cjs/node_modules/crypto-js/x64-core.js +0 -6
- package/dist/cjs/node_modules/dfa/index.js +0 -6
- package/dist/cjs/node_modules/emoji-regex/index.js +0 -6
- package/dist/cjs/node_modules/events/events.js +0 -6
- package/dist/cjs/node_modules/fast-deep-equal/index.js +0 -6
- package/dist/cjs/node_modules/fontkit/dist/browser-module.js +0 -6
- package/dist/cjs/node_modules/hsl-to-hex/index.js +0 -6
- package/dist/cjs/node_modules/hsl-to-rgb-for-reals/converter.js +0 -6
- package/dist/cjs/node_modules/hyphen/hyphen.js +0 -6
- package/dist/cjs/node_modules/hyphen/patterns/en-us.js +0 -6
- package/dist/cjs/node_modules/inherits/inherits_browser.js +0 -6
- package/dist/cjs/node_modules/jay-peg/src/index.js +0 -6
- package/dist/cjs/node_modules/jay-peg/src/markers/dac.js +0 -6
- package/dist/cjs/node_modules/jay-peg/src/markers/dht.js +0 -6
- package/dist/cjs/node_modules/jay-peg/src/markers/dqt.js +0 -6
- package/dist/cjs/node_modules/jay-peg/src/markers/dri.js +0 -6
- package/dist/cjs/node_modules/jay-peg/src/markers/eoi.js +0 -6
- package/dist/cjs/node_modules/jay-peg/src/markers/exif.js +0 -6
- package/dist/cjs/node_modules/jay-peg/src/markers/jfif.js +0 -6
- package/dist/cjs/node_modules/jay-peg/src/markers/sof.js +0 -6
- package/dist/cjs/node_modules/jay-peg/src/markers/soi.js +0 -6
- package/dist/cjs/node_modules/jay-peg/src/markers/sos.js +0 -6
- package/dist/cjs/node_modules/jay-peg/src/markers/utils.js +0 -6
- package/dist/cjs/node_modules/linebreak/dist/module.js +0 -6
- package/dist/cjs/node_modules/linebreak/node_modules/base64-js/lib/b64.js +0 -6
- package/dist/cjs/node_modules/media-engine/src/index.js +0 -6
- package/dist/cjs/node_modules/media-engine/src/operators.js +0 -6
- package/dist/cjs/node_modules/media-engine/src/parser.js +0 -6
- package/dist/cjs/node_modules/media-engine/src/queries.js +0 -6
- package/dist/cjs/node_modules/normalize-svg-path/index.js +0 -6
- package/dist/cjs/node_modules/pako/lib/utils/common.js +0 -6
- package/dist/cjs/node_modules/pako/lib/zlib/adler32.js +0 -6
- package/dist/cjs/node_modules/pako/lib/zlib/constants.js +0 -6
- package/dist/cjs/node_modules/pako/lib/zlib/crc32.js +0 -6
- package/dist/cjs/node_modules/pako/lib/zlib/deflate.js +0 -6
- package/dist/cjs/node_modules/pako/lib/zlib/inffast.js +0 -6
- package/dist/cjs/node_modules/pako/lib/zlib/inflate.js +0 -6
- package/dist/cjs/node_modules/pako/lib/zlib/inftrees.js +0 -6
- package/dist/cjs/node_modules/pako/lib/zlib/messages.js +0 -6
- package/dist/cjs/node_modules/pako/lib/zlib/trees.js +0 -6
- package/dist/cjs/node_modules/pako/lib/zlib/zstream.js +0 -6
- package/dist/cjs/node_modules/parse-svg-path/index.js +0 -6
- package/dist/cjs/node_modules/postcss-value-parser/lib/parse.js +0 -6
- package/dist/cjs/node_modules/postcss-value-parser/lib/unit.js +0 -6
- package/dist/cjs/node_modules/queue/index.js +0 -6
- package/dist/cjs/node_modules/restructure/src/Array.js +0 -6
- package/dist/cjs/node_modules/restructure/src/Base.js +0 -6
- package/dist/cjs/node_modules/restructure/src/Bitfield.js +0 -6
- package/dist/cjs/node_modules/restructure/src/Buffer.js +0 -6
- package/dist/cjs/node_modules/restructure/src/DecodeStream.js +0 -6
- package/dist/cjs/node_modules/restructure/src/EncodeStream.js +0 -6
- package/dist/cjs/node_modules/restructure/src/LazyArray.js +0 -6
- package/dist/cjs/node_modules/restructure/src/Number.js +0 -6
- package/dist/cjs/node_modules/restructure/src/Optional.js +0 -6
- package/dist/cjs/node_modules/restructure/src/Pointer.js +0 -6
- package/dist/cjs/node_modules/restructure/src/Reserved.js +0 -6
- package/dist/cjs/node_modules/restructure/src/String.js +0 -6
- package/dist/cjs/node_modules/restructure/src/Struct.js +0 -6
- package/dist/cjs/node_modules/restructure/src/VersionedStruct.js +0 -6
- package/dist/cjs/node_modules/restructure/src/utils.js +0 -6
- package/dist/cjs/node_modules/scheduler/cjs/scheduler.development.js +0 -15
- package/dist/cjs/node_modules/scheduler/cjs/scheduler.production.js +0 -15
- package/dist/cjs/node_modules/scheduler/index.js +0 -6
- package/dist/cjs/node_modules/simple-swizzle/index.js +0 -6
- package/dist/cjs/node_modules/simple-swizzle/node_modules/is-arrayish/index.js +0 -6
- package/dist/cjs/node_modules/svg-arc-to-cubic-bezier/modules/index.js +0 -6
- package/dist/cjs/node_modules/tiny-inflate/index.js +0 -6
- package/dist/cjs/node_modules/tslib/tslib.es6.js +0 -6
- package/dist/cjs/node_modules/unicode-properties/dist/module.js +0 -6
- package/dist/cjs/node_modules/unicode-trie/index.js +0 -6
- package/dist/cjs/node_modules/unicode-trie/swap.js +0 -6
- package/dist/cjs/node_modules/yoga-layout/dist/binaries/yoga-wasm-base64-esm.js +0 -6
- package/dist/cjs/node_modules/yoga-layout/dist/src/generated/YGEnums.js +0 -6
- package/dist/cjs/node_modules/yoga-layout/dist/src/load.js +0 -6
- package/dist/cjs/node_modules/yoga-layout/dist/src/wrapAssembly.js +0 -6
- package/dist/cjs/src/components/e-commerce/invoice/InvoicePDF.js +0 -7
- package/dist/esm/_virtual/_commonjs-dynamic-modules.js +0 -11
- package/dist/esm/_virtual/aes.js +0 -9
- package/dist/esm/_virtual/b64.js +0 -9
- package/dist/esm/_virtual/blowfish.js +0 -9
- package/dist/esm/_virtual/cipher-core.js +0 -9
- package/dist/esm/_virtual/clone.js +0 -9
- package/dist/esm/_virtual/common.js +0 -9
- package/dist/esm/_virtual/context.js +0 -9
- package/dist/esm/_virtual/core2.js +0 -9
- package/dist/esm/_virtual/decode.js +0 -9
- package/dist/esm/_virtual/deflate.js +0 -9
- package/dist/esm/_virtual/dictionary-browser.js +0 -9
- package/dist/esm/_virtual/dictionary.js +0 -9
- package/dist/esm/_virtual/en-us.js +0 -9
- package/dist/esm/_virtual/enc-base64.js +0 -9
- package/dist/esm/_virtual/enc-base64url.js +0 -9
- package/dist/esm/_virtual/enc-utf16.js +0 -9
- package/dist/esm/_virtual/events.js +0 -9
- package/dist/esm/_virtual/evpkdf.js +0 -9
- package/dist/esm/_virtual/format-hex.js +0 -9
- package/dist/esm/_virtual/hmac.js +0 -9
- package/dist/esm/_virtual/huffman.js +0 -9
- package/dist/esm/_virtual/hyphen.js +0 -9
- package/dist/esm/_virtual/index10.js +0 -9
- package/dist/esm/_virtual/index5.js +0 -9
- package/dist/esm/_virtual/index6.js +0 -9
- package/dist/esm/_virtual/index7.js +0 -9
- package/dist/esm/_virtual/index8.js +0 -9
- package/dist/esm/_virtual/index9.js +0 -9
- package/dist/esm/_virtual/inflate.js +0 -9
- package/dist/esm/_virtual/inherits_browser.js +0 -9
- package/dist/esm/_virtual/lib-typedarrays.js +0 -9
- package/dist/esm/_virtual/md5.js +0 -9
- package/dist/esm/_virtual/mode-cfb.js +0 -9
- package/dist/esm/_virtual/mode-ctr-gladman.js +0 -9
- package/dist/esm/_virtual/mode-ctr.js +0 -9
- package/dist/esm/_virtual/mode-ecb.js +0 -9
- package/dist/esm/_virtual/mode-ofb.js +0 -9
- package/dist/esm/_virtual/pad-ansix923.js +0 -9
- package/dist/esm/_virtual/pad-iso10126.js +0 -9
- package/dist/esm/_virtual/pad-iso97971.js +0 -9
- package/dist/esm/_virtual/pad-nopadding.js +0 -9
- package/dist/esm/_virtual/pad-zeropadding.js +0 -9
- package/dist/esm/_virtual/pbkdf2.js +0 -9
- package/dist/esm/_virtual/prefix.js +0 -9
- package/dist/esm/_virtual/rabbit-legacy.js +0 -9
- package/dist/esm/_virtual/rabbit.js +0 -9
- package/dist/esm/_virtual/rc4.js +0 -9
- package/dist/esm/_virtual/ripemd160.js +0 -9
- package/dist/esm/_virtual/scheduler.development.js +0 -9
- package/dist/esm/_virtual/scheduler.production.js +0 -9
- package/dist/esm/_virtual/sha1.js +0 -9
- package/dist/esm/_virtual/sha224.js +0 -9
- package/dist/esm/_virtual/sha256.js +0 -9
- package/dist/esm/_virtual/sha3.js +0 -9
- package/dist/esm/_virtual/sha384.js +0 -9
- package/dist/esm/_virtual/sha512.js +0 -9
- package/dist/esm/_virtual/streams.js +0 -9
- package/dist/esm/_virtual/transform.js +0 -9
- package/dist/esm/_virtual/trees.js +0 -9
- package/dist/esm/_virtual/tripledes.js +0 -9
- package/dist/esm/_virtual/x64-core.js +0 -9
- package/dist/esm/node_modules/@react-pdf/fns/lib/index.js +0 -254
- package/dist/esm/node_modules/@react-pdf/font/lib/index.browser.js +0 -481
- package/dist/esm/node_modules/@react-pdf/image/lib/index.browser.js +0 -2217
- package/dist/esm/node_modules/@react-pdf/layout/lib/index.js +0 -2982
- package/dist/esm/node_modules/@react-pdf/pdfkit/lib/pdfkit.browser.js +0 -47429
- package/dist/esm/node_modules/@react-pdf/png-js/lib/png-js.browser.js +0 -12606
- package/dist/esm/node_modules/@react-pdf/primitives/lib/index.js +0 -37
- package/dist/esm/node_modules/@react-pdf/reconciler/lib/index.js +0 -16
- package/dist/esm/node_modules/@react-pdf/reconciler/lib/reconciler-23.js +0 -22
- package/dist/esm/node_modules/@react-pdf/reconciler/lib/reconciler-31.js +0 -30
- package/dist/esm/node_modules/@react-pdf/render/lib/index.js +0 -2081
- package/dist/esm/node_modules/@react-pdf/renderer/lib/react-pdf.browser.js +0 -472
- package/dist/esm/node_modules/@react-pdf/stylesheet/lib/index.js +0 -771
- package/dist/esm/node_modules/@react-pdf/textkit/lib/textkit.js +0 -2854
- package/dist/esm/node_modules/@swc/helpers/esm/_define_property.js +0 -15
- package/dist/esm/node_modules/abs-svg-path/index.js +0 -78
- package/dist/esm/node_modules/base64-js/index.js +0 -158
- package/dist/esm/node_modules/bidi-js/dist/bidi.js +0 -1008
- package/dist/esm/node_modules/brotli/dec/bit_reader.js +0 -132
- package/dist/esm/node_modules/brotli/dec/context.js +0 -255
- package/dist/esm/node_modules/brotli/dec/decode.js +0 -965
- package/dist/esm/node_modules/brotli/dec/dictionary-browser.js +0 -35
- package/dist/esm/node_modules/brotli/dec/dictionary.bin.js +0 -17
- package/dist/esm/node_modules/brotli/dec/dictionary.js +0 -54
- package/dist/esm/node_modules/brotli/dec/huffman.js +0 -133
- package/dist/esm/node_modules/brotli/dec/prefix.js +0 -70
- package/dist/esm/node_modules/brotli/dec/streams.js +0 -44
- package/dist/esm/node_modules/brotli/dec/transform.js +0 -264
- package/dist/esm/node_modules/brotli/decompress.js +0 -14
- package/dist/esm/node_modules/clone/clone.js +0 -272
- package/dist/esm/node_modules/color-name/index.js +0 -158
- package/dist/esm/node_modules/color-string/index.js +0 -259
- package/dist/esm/node_modules/crypto-js/aes.js +0 -251
- package/dist/esm/node_modules/crypto-js/blowfish.js +0 -488
- package/dist/esm/node_modules/crypto-js/cipher-core.js +0 -909
- package/dist/esm/node_modules/crypto-js/core.js +0 -820
- package/dist/esm/node_modules/crypto-js/enc-base64.js +0 -149
- package/dist/esm/node_modules/crypto-js/enc-base64url.js +0 -161
- package/dist/esm/node_modules/crypto-js/enc-utf16.js +0 -162
- package/dist/esm/node_modules/crypto-js/evpkdf.js +0 -149
- package/dist/esm/node_modules/crypto-js/format-hex.js +0 -80
- package/dist/esm/node_modules/crypto-js/hmac.js +0 -156
- package/dist/esm/node_modules/crypto-js/index.js +0 -61
- package/dist/esm/node_modules/crypto-js/lib-typedarrays.js +0 -89
- package/dist/esm/node_modules/crypto-js/md5.js +0 -277
- package/dist/esm/node_modules/crypto-js/mode-cfb.js +0 -94
- package/dist/esm/node_modules/crypto-js/mode-ctr-gladman.js +0 -130
- package/dist/esm/node_modules/crypto-js/mode-ctr.js +0 -72
- package/dist/esm/node_modules/crypto-js/mode-ecb.js +0 -54
- package/dist/esm/node_modules/crypto-js/mode-ofb.js +0 -68
- package/dist/esm/node_modules/crypto-js/pad-ansix923.js +0 -63
- package/dist/esm/node_modules/crypto-js/pad-iso10126.js +0 -58
- package/dist/esm/node_modules/crypto-js/pad-iso97971.js +0 -54
- package/dist/esm/node_modules/crypto-js/pad-nopadding.js +0 -44
- package/dist/esm/node_modules/crypto-js/pad-zeropadding.js +0 -61
- package/dist/esm/node_modules/crypto-js/pbkdf2.js +0 -160
- package/dist/esm/node_modules/crypto-js/rabbit-legacy.js +0 -207
- package/dist/esm/node_modules/crypto-js/rabbit.js +0 -209
- package/dist/esm/node_modules/crypto-js/rc4.js +0 -156
- package/dist/esm/node_modules/crypto-js/ripemd160.js +0 -280
- package/dist/esm/node_modules/crypto-js/sha1.js +0 -163
- package/dist/esm/node_modules/crypto-js/sha224.js +0 -94
- package/dist/esm/node_modules/crypto-js/sha256.js +0 -212
- package/dist/esm/node_modules/crypto-js/sha3.js +0 -340
- package/dist/esm/node_modules/crypto-js/sha384.js +0 -98
- package/dist/esm/node_modules/crypto-js/sha512.js +0 -340
- package/dist/esm/node_modules/crypto-js/tripledes.js +0 -796
- package/dist/esm/node_modules/crypto-js/x64-core.js +0 -317
- package/dist/esm/node_modules/dfa/index.js +0 -101
- package/dist/esm/node_modules/emoji-regex/index.js +0 -12
- package/dist/esm/node_modules/events/events.js +0 -485
- package/dist/esm/node_modules/fast-deep-equal/index.js +0 -56
- package/dist/esm/node_modules/fontkit/dist/browser-module.js +0 -13295
- package/dist/esm/node_modules/hsl-to-hex/index.js +0 -72
- package/dist/esm/node_modules/hsl-to-rgb-for-reals/converter.js +0 -66
- package/dist/esm/node_modules/hyphen/hyphen.js +0 -424
- package/dist/esm/node_modules/hyphen/patterns/en-us.js +0 -48
- package/dist/esm/node_modules/inherits/inherits_browser.js +0 -39
- package/dist/esm/node_modules/jay-peg/src/index.js +0 -68
- package/dist/esm/node_modules/jay-peg/src/markers/dac.js +0 -25
- package/dist/esm/node_modules/jay-peg/src/markers/dht.js +0 -52
- package/dist/esm/node_modules/jay-peg/src/markers/dqt.js +0 -26
- package/dist/esm/node_modules/jay-peg/src/markers/dri.js +0 -17
- package/dist/esm/node_modules/jay-peg/src/markers/eoi.js +0 -17
- package/dist/esm/node_modules/jay-peg/src/markers/exif.js +0 -327
- package/dist/esm/node_modules/jay-peg/src/markers/jfif.js +0 -24
- package/dist/esm/node_modules/jay-peg/src/markers/sof.js +0 -32
- package/dist/esm/node_modules/jay-peg/src/markers/soi.js +0 -11
- package/dist/esm/node_modules/jay-peg/src/markers/sos.js +0 -57
- package/dist/esm/node_modules/jay-peg/src/markers/utils.js +0 -71
- package/dist/esm/node_modules/linebreak/dist/module.js +0 -1340
- package/dist/esm/node_modules/linebreak/node_modules/base64-js/lib/b64.js +0 -137
- package/dist/esm/node_modules/media-engine/src/index.js +0 -26
- package/dist/esm/node_modules/media-engine/src/operators.js +0 -36
- package/dist/esm/node_modules/media-engine/src/parser.js +0 -147
- package/dist/esm/node_modules/media-engine/src/queries.js +0 -64
- package/dist/esm/node_modules/normalize-svg-path/index.js +0 -126
- package/dist/esm/node_modules/pako/lib/utils/common.js +0 -114
- package/dist/esm/node_modules/pako/lib/zlib/adler32.js +0 -57
- package/dist/esm/node_modules/pako/lib/zlib/constants.js +0 -78
- package/dist/esm/node_modules/pako/lib/zlib/crc32.js +0 -65
- package/dist/esm/node_modules/pako/lib/zlib/deflate.js +0 -1880
- package/dist/esm/node_modules/pako/lib/zlib/inffast.js +0 -351
- package/dist/esm/node_modules/pako/lib/zlib/inflate.js +0 -1557
- package/dist/esm/node_modules/pako/lib/zlib/inftrees.js +0 -352
- package/dist/esm/node_modules/pako/lib/zlib/messages.js +0 -38
- package/dist/esm/node_modules/pako/lib/zlib/trees.js +0 -1232
- package/dist/esm/node_modules/pako/lib/zlib/zstream.js +0 -57
- package/dist/esm/node_modules/parse-svg-path/index.js +0 -68
- package/dist/esm/node_modules/postcss-value-parser/lib/parse.js +0 -333
- package/dist/esm/node_modules/postcss-value-parser/lib/unit.js +0 -132
- package/dist/esm/node_modules/queue/index.js +0 -211
- package/dist/esm/node_modules/restructure/src/Array.js +0 -112
- package/dist/esm/node_modules/restructure/src/Base.js +0 -25
- package/dist/esm/node_modules/restructure/src/Bitfield.js +0 -47
- package/dist/esm/node_modules/restructure/src/Buffer.js +0 -44
- package/dist/esm/node_modules/restructure/src/DecodeStream.js +0 -93
- package/dist/esm/node_modules/restructure/src/EncodeStream.js +0 -137
- package/dist/esm/node_modules/restructure/src/LazyArray.js +0 -82
- package/dist/esm/node_modules/restructure/src/Number.js +0 -80
- package/dist/esm/node_modules/restructure/src/Optional.js +0 -52
- package/dist/esm/node_modules/restructure/src/Pointer.js +0 -175
- package/dist/esm/node_modules/restructure/src/Reserved.js +0 -31
- package/dist/esm/node_modules/restructure/src/String.js +0 -161
- package/dist/esm/node_modules/restructure/src/Struct.js +0 -121
- package/dist/esm/node_modules/restructure/src/VersionedStruct.js +0 -147
- package/dist/esm/node_modules/restructure/src/utils.js +0 -42
- package/dist/esm/node_modules/scheduler/cjs/scheduler.development.js +0 -382
- package/dist/esm/node_modules/scheduler/cjs/scheduler.production.js +0 -359
- package/dist/esm/node_modules/scheduler/index.js +0 -21
- package/dist/esm/node_modules/simple-swizzle/index.js +0 -40
- package/dist/esm/node_modules/simple-swizzle/node_modules/is-arrayish/index.js +0 -17
- package/dist/esm/node_modules/svg-arc-to-cubic-bezier/modules/index.js +0 -189
- package/dist/esm/node_modules/tiny-inflate/index.js +0 -387
- package/dist/esm/node_modules/tslib/tslib.es6.js +0 -36
- package/dist/esm/node_modules/unicode-properties/dist/module.js +0 -132
- package/dist/esm/node_modules/unicode-trie/index.js +0 -150
- package/dist/esm/node_modules/unicode-trie/swap.js +0 -33
- package/dist/esm/node_modules/yoga-layout/dist/binaries/yoga-wasm-base64-esm.js +0 -81
- package/dist/esm/node_modules/yoga-layout/dist/src/generated/YGEnums.js +0 -217
- package/dist/esm/node_modules/yoga-layout/dist/src/load.js +0 -24
- package/dist/esm/node_modules/yoga-layout/dist/src/wrapAssembly.js +0 -130
- package/dist/esm/src/components/e-commerce/invoice/InvoicePDF.js +0 -181
|
@@ -1,2854 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* UMWD-Components
|
|
3
|
-
* @copyright Jelle Paulus
|
|
4
|
-
* @license MIT
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { compose, isNil, repeat, last, dropLast as dropLast$2, adjust, reverse } from '../../fns/lib/index.js';
|
|
8
|
-
import bidiFactory from '../../../bidi-js/dist/bidi.js';
|
|
9
|
-
import $747425b437e121da$export$2e2bcd8739ae039 from '../../../unicode-properties/dist/module.js';
|
|
10
|
-
import hyphen from '../../../hyphen/hyphen.js';
|
|
11
|
-
import pattern from '../../../hyphen/patterns/en-us.js';
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Create attributed string from text fragments
|
|
15
|
-
*
|
|
16
|
-
* @param fragments - Fragments
|
|
17
|
-
* @returns Attributed string
|
|
18
|
-
*/
|
|
19
|
-
const fromFragments = (fragments) => {
|
|
20
|
-
let offset = 0;
|
|
21
|
-
let string = '';
|
|
22
|
-
const runs = [];
|
|
23
|
-
fragments.forEach((fragment) => {
|
|
24
|
-
string += fragment.string;
|
|
25
|
-
runs.push({
|
|
26
|
-
...fragment,
|
|
27
|
-
start: offset,
|
|
28
|
-
end: offset + fragment.string.length,
|
|
29
|
-
attributes: fragment.attributes || {},
|
|
30
|
-
});
|
|
31
|
-
offset += fragment.string.length;
|
|
32
|
-
});
|
|
33
|
-
return { string, runs };
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Default word hyphenation engine used when no one provided.
|
|
38
|
-
* Does not perform word hyphenation at all
|
|
39
|
-
*
|
|
40
|
-
* @param word
|
|
41
|
-
* @returns Same word
|
|
42
|
-
*/
|
|
43
|
-
const defaultHyphenationEngine = (word) => [word];
|
|
44
|
-
/**
|
|
45
|
-
* Wrap words of attribute string
|
|
46
|
-
*
|
|
47
|
-
* @param engines layout engines
|
|
48
|
-
* @param options layout options
|
|
49
|
-
*/
|
|
50
|
-
const wrapWords = (engines = {}, options = {}) => {
|
|
51
|
-
/**
|
|
52
|
-
* @param attributedString - Attributed string
|
|
53
|
-
* @returns Attributed string including syllables
|
|
54
|
-
*/
|
|
55
|
-
return (attributedString) => {
|
|
56
|
-
const syllables = [];
|
|
57
|
-
const fragments = [];
|
|
58
|
-
const hyphenateWord = options.hyphenationCallback ||
|
|
59
|
-
engines.wordHyphenation?.() ||
|
|
60
|
-
defaultHyphenationEngine;
|
|
61
|
-
for (let i = 0; i < attributedString.runs.length; i += 1) {
|
|
62
|
-
let string = '';
|
|
63
|
-
const run = attributedString.runs[i];
|
|
64
|
-
const words = attributedString.string
|
|
65
|
-
.slice(run.start, run.end)
|
|
66
|
-
.split(/([ ]+)/g)
|
|
67
|
-
.filter(Boolean);
|
|
68
|
-
for (let j = 0; j < words.length; j += 1) {
|
|
69
|
-
const word = words[j];
|
|
70
|
-
const parts = hyphenateWord(word);
|
|
71
|
-
syllables.push(...parts);
|
|
72
|
-
string += parts.join('');
|
|
73
|
-
}
|
|
74
|
-
fragments.push({ ...run, string });
|
|
75
|
-
}
|
|
76
|
-
const result = { ...fromFragments(fragments), syllables };
|
|
77
|
-
return result;
|
|
78
|
-
};
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Clone rect
|
|
83
|
-
*
|
|
84
|
-
* @param rect - Rect
|
|
85
|
-
* @returns Cloned rect
|
|
86
|
-
*/
|
|
87
|
-
const copy = (rect) => {
|
|
88
|
-
return Object.assign({}, rect);
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Partition rect in two in the vertical direction
|
|
93
|
-
*
|
|
94
|
-
* @param rect - Rect
|
|
95
|
-
* @param height - Height
|
|
96
|
-
* @returns Partitioned rects
|
|
97
|
-
*/
|
|
98
|
-
const partition = (rect, height) => {
|
|
99
|
-
const a = Object.assign({}, rect, { height });
|
|
100
|
-
const b = Object.assign({}, rect, {
|
|
101
|
-
y: rect.y + height,
|
|
102
|
-
height: rect.height - height,
|
|
103
|
-
});
|
|
104
|
-
return [a, b];
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Crop upper section of rect
|
|
109
|
-
*
|
|
110
|
-
* @param height - Height
|
|
111
|
-
* @param rect - Rect
|
|
112
|
-
* @returns Cropped rect
|
|
113
|
-
*/
|
|
114
|
-
const crop = (height, rect) => {
|
|
115
|
-
const [, result] = partition(rect, height);
|
|
116
|
-
return result;
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Get paragraph block height
|
|
121
|
-
*
|
|
122
|
-
* @param paragraph - Paragraph
|
|
123
|
-
* @returns Paragraph block height
|
|
124
|
-
*/
|
|
125
|
-
const height$2 = (paragraph) => {
|
|
126
|
-
return paragraph.reduce((acc, block) => acc + block.box.height, 0);
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Calculate run scale
|
|
131
|
-
*
|
|
132
|
-
* @param run - Run
|
|
133
|
-
* @returns Scale
|
|
134
|
-
*/
|
|
135
|
-
const calculateScale = (run) => {
|
|
136
|
-
const attributes = run.attributes || {};
|
|
137
|
-
const fontSize = attributes.fontSize || 12;
|
|
138
|
-
const font = attributes.font;
|
|
139
|
-
const unitsPerEm = typeof font === 'string' ? null : font?.[0]?.unitsPerEm;
|
|
140
|
-
return unitsPerEm ? fontSize / unitsPerEm : 0;
|
|
141
|
-
};
|
|
142
|
-
/**
|
|
143
|
-
* Get run scale
|
|
144
|
-
*
|
|
145
|
-
* @param run
|
|
146
|
-
* @returns Scale
|
|
147
|
-
*/
|
|
148
|
-
const scale = (run) => {
|
|
149
|
-
return run.attributes?.scale || calculateScale(run);
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Get ligature offset by index
|
|
154
|
-
*
|
|
155
|
-
* Ex. ffi ligature
|
|
156
|
-
*
|
|
157
|
-
* glyphs: l o f f i m
|
|
158
|
-
* glyphIndices: 0 1 2 2 2 3
|
|
159
|
-
* offset: 0 0 0 1 2 0
|
|
160
|
-
*
|
|
161
|
-
* @param index
|
|
162
|
-
* @param run - Run
|
|
163
|
-
* @returns Ligature offset
|
|
164
|
-
*/
|
|
165
|
-
const offset = (index, run) => {
|
|
166
|
-
if (!run)
|
|
167
|
-
return 0;
|
|
168
|
-
const glyphIndices = run.glyphIndices || [];
|
|
169
|
-
const value = glyphIndices[index];
|
|
170
|
-
return glyphIndices.slice(0, index).filter((i) => i === value).length;
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Get run font
|
|
175
|
-
*
|
|
176
|
-
* @param run - Run
|
|
177
|
-
* @returns Font
|
|
178
|
-
*/
|
|
179
|
-
const getFont = (run) => {
|
|
180
|
-
return run.attributes?.font?.[0] || null;
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Slice glyph between codePoints range
|
|
185
|
-
* Util for breaking ligatures
|
|
186
|
-
*
|
|
187
|
-
* @param start - Start code point index
|
|
188
|
-
* @param end - End code point index
|
|
189
|
-
* @param font - Font to generate new glyph
|
|
190
|
-
* @param glyph - Glyph to be sliced
|
|
191
|
-
* @returns Sliced glyph parts
|
|
192
|
-
*/
|
|
193
|
-
const slice$2 = (start, end, font, glyph) => {
|
|
194
|
-
if (!glyph)
|
|
195
|
-
return [];
|
|
196
|
-
if (start === end)
|
|
197
|
-
return [];
|
|
198
|
-
if (start === 0 && end === glyph.codePoints.length)
|
|
199
|
-
return [glyph];
|
|
200
|
-
const codePoints = glyph.codePoints.slice(start, end);
|
|
201
|
-
const string = String.fromCodePoint(...codePoints);
|
|
202
|
-
// passing LTR To force fontkit to not reverse the string
|
|
203
|
-
return font
|
|
204
|
-
? font.layout(string, undefined, undefined, undefined, 'ltr').glyphs
|
|
205
|
-
: [glyph];
|
|
206
|
-
};
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* Return glyph index at string index, if glyph indices present.
|
|
210
|
-
* Otherwise return string index
|
|
211
|
-
*
|
|
212
|
-
* @param index - Index
|
|
213
|
-
* @param run - Run
|
|
214
|
-
* @returns Glyph index
|
|
215
|
-
*/
|
|
216
|
-
const glyphIndexAt = (index, run) => {
|
|
217
|
-
const result = run?.glyphIndices?.[index];
|
|
218
|
-
return isNil(result) ? index : result;
|
|
219
|
-
};
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* Returns new array starting with zero, and keeping same relation between consecutive values
|
|
223
|
-
*
|
|
224
|
-
* @param array - List
|
|
225
|
-
* @returns Normalized array
|
|
226
|
-
*/
|
|
227
|
-
const normalize = (array) => {
|
|
228
|
-
const head = array[0];
|
|
229
|
-
return array.map((value) => value - head);
|
|
230
|
-
};
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Slice run between glyph indices range
|
|
234
|
-
*
|
|
235
|
-
* @param start - Glyph index
|
|
236
|
-
* @param end - Glyph index
|
|
237
|
-
* @param run - Run
|
|
238
|
-
* @returns Sliced run
|
|
239
|
-
*/
|
|
240
|
-
const slice$1 = (start, end, run) => {
|
|
241
|
-
const runScale = scale(run);
|
|
242
|
-
const font = getFont(run);
|
|
243
|
-
// Get glyph start and end indices
|
|
244
|
-
const startIndex = glyphIndexAt(start, run);
|
|
245
|
-
const endIndex = glyphIndexAt(end, run);
|
|
246
|
-
// Get start and end glyph
|
|
247
|
-
const startGlyph = run.glyphs?.[startIndex];
|
|
248
|
-
const endGlyph = run.glyphs?.[endIndex];
|
|
249
|
-
// Get start ligature chunks (if any)
|
|
250
|
-
const startOffset = offset(start, run);
|
|
251
|
-
const startGlyphs = startOffset > 0 ? slice$2(startOffset, Infinity, font, startGlyph) : [];
|
|
252
|
-
// Get end ligature chunks (if any)
|
|
253
|
-
const endOffset = offset(end, run);
|
|
254
|
-
const endGlyphs = slice$2(0, endOffset, font, endGlyph);
|
|
255
|
-
// Compute new glyphs
|
|
256
|
-
const sliceStart = startIndex + Math.min(1, startOffset);
|
|
257
|
-
const glyphs = (run.glyphs || []).slice(sliceStart, endIndex);
|
|
258
|
-
// Compute new positions
|
|
259
|
-
const glyphPosition = (g) => ({
|
|
260
|
-
xAdvance: g.advanceWidth * runScale,
|
|
261
|
-
yAdvance: 0,
|
|
262
|
-
xOffset: 0,
|
|
263
|
-
yOffset: 0,
|
|
264
|
-
});
|
|
265
|
-
const startPositions = startGlyphs.map(glyphPosition);
|
|
266
|
-
const positions = (run.positions || []).slice(sliceStart, endIndex);
|
|
267
|
-
const endPositions = endGlyphs.map(glyphPosition);
|
|
268
|
-
return Object.assign({}, run, {
|
|
269
|
-
start: run.start + start,
|
|
270
|
-
end: Math.min(run.end, run.start + end),
|
|
271
|
-
glyphIndices: normalize((run.glyphIndices || []).slice(start, end)),
|
|
272
|
-
glyphs: [startGlyphs, glyphs, endGlyphs].flat(),
|
|
273
|
-
positions: [startPositions, positions, endPositions].flat(),
|
|
274
|
-
});
|
|
275
|
-
};
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
* Get run index that contains passed index
|
|
279
|
-
*
|
|
280
|
-
* @param index - Index
|
|
281
|
-
* @param runs - Runs
|
|
282
|
-
* @returns Run index
|
|
283
|
-
*/
|
|
284
|
-
const runIndexAt$1 = (index, runs) => {
|
|
285
|
-
if (!runs)
|
|
286
|
-
return -1;
|
|
287
|
-
return runs.findIndex((run) => run.start <= index && index < run.end);
|
|
288
|
-
};
|
|
289
|
-
|
|
290
|
-
/**
|
|
291
|
-
* Filter runs contained between start and end
|
|
292
|
-
*
|
|
293
|
-
* @param start
|
|
294
|
-
* @param end
|
|
295
|
-
* @param runs
|
|
296
|
-
* @returns Filtered runs
|
|
297
|
-
*/
|
|
298
|
-
const filter = (start, end, runs) => {
|
|
299
|
-
const startIndex = runIndexAt$1(start, runs);
|
|
300
|
-
const endIndex = Math.max(runIndexAt$1(end - 1, runs), startIndex);
|
|
301
|
-
return runs.slice(startIndex, endIndex + 1);
|
|
302
|
-
};
|
|
303
|
-
|
|
304
|
-
/**
|
|
305
|
-
* Subtract scalar to run
|
|
306
|
-
*
|
|
307
|
-
* @param index - Scalar
|
|
308
|
-
* @param run - Run
|
|
309
|
-
* @returns Subtracted run
|
|
310
|
-
*/
|
|
311
|
-
const subtract = (index, run) => {
|
|
312
|
-
const start = run.start - index;
|
|
313
|
-
const end = run.end - index;
|
|
314
|
-
return Object.assign({}, run, { start, end });
|
|
315
|
-
};
|
|
316
|
-
|
|
317
|
-
/**
|
|
318
|
-
* Slice array of runs
|
|
319
|
-
*
|
|
320
|
-
* @param start - Offset
|
|
321
|
-
* @param end - Offset
|
|
322
|
-
* @param runs
|
|
323
|
-
* @returns Sliced runs
|
|
324
|
-
*/
|
|
325
|
-
const sliceRuns = (start, end, runs) => {
|
|
326
|
-
const sliceFirstRun = (a) => slice$1(start - a.start, end - a.start, a);
|
|
327
|
-
const sliceLastRun = (a) => slice$1(0, end - a.start, a);
|
|
328
|
-
return runs.map((run, i) => {
|
|
329
|
-
let result = run;
|
|
330
|
-
const isFirst = i === 0;
|
|
331
|
-
const isLast = !isFirst && i === runs.length - 1;
|
|
332
|
-
if (isFirst)
|
|
333
|
-
result = sliceFirstRun(run);
|
|
334
|
-
if (isLast)
|
|
335
|
-
result = sliceLastRun(run);
|
|
336
|
-
return subtract(start, result);
|
|
337
|
-
});
|
|
338
|
-
};
|
|
339
|
-
/**
|
|
340
|
-
* Slice attributed string between two indices
|
|
341
|
-
*
|
|
342
|
-
* @param start - Offset
|
|
343
|
-
* @param end - Offset
|
|
344
|
-
* @param attributedString - Attributed string
|
|
345
|
-
* @returns Attributed string
|
|
346
|
-
*/
|
|
347
|
-
const slice = (start, end, attributedString) => {
|
|
348
|
-
if (attributedString.string.length === 0)
|
|
349
|
-
return attributedString;
|
|
350
|
-
const string = attributedString.string.slice(start, end);
|
|
351
|
-
const filteredRuns = filter(start, end, attributedString.runs);
|
|
352
|
-
const slicedRuns = sliceRuns(start, end, filteredRuns);
|
|
353
|
-
return Object.assign({}, attributedString, { string, runs: slicedRuns });
|
|
354
|
-
};
|
|
355
|
-
|
|
356
|
-
const findCharIndex = (string) => {
|
|
357
|
-
return string.search(/\S/g);
|
|
358
|
-
};
|
|
359
|
-
const findLastCharIndex = (string) => {
|
|
360
|
-
const match = string.match(/\S/g);
|
|
361
|
-
return match ? string.lastIndexOf(match[match.length - 1]) : -1;
|
|
362
|
-
};
|
|
363
|
-
/**
|
|
364
|
-
* Removes (strips) whitespace from both ends of the attributted string.
|
|
365
|
-
*
|
|
366
|
-
* @param attributedString - Attributed string
|
|
367
|
-
* @returns Attributed string
|
|
368
|
-
*/
|
|
369
|
-
const trim = (attributedString) => {
|
|
370
|
-
const start = findCharIndex(attributedString.string);
|
|
371
|
-
const end = findLastCharIndex(attributedString.string);
|
|
372
|
-
return slice(start, end + 1, attributedString);
|
|
373
|
-
};
|
|
374
|
-
|
|
375
|
-
/**
|
|
376
|
-
* Returns empty run
|
|
377
|
-
*
|
|
378
|
-
* @returns Empty run
|
|
379
|
-
*/
|
|
380
|
-
const empty$1 = () => {
|
|
381
|
-
return {
|
|
382
|
-
start: 0,
|
|
383
|
-
end: 0,
|
|
384
|
-
glyphIndices: [],
|
|
385
|
-
glyphs: [],
|
|
386
|
-
positions: [],
|
|
387
|
-
attributes: {},
|
|
388
|
-
};
|
|
389
|
-
};
|
|
390
|
-
|
|
391
|
-
/**
|
|
392
|
-
* Check if value is a number
|
|
393
|
-
*
|
|
394
|
-
* @param value - Value to check
|
|
395
|
-
* @returns Whether value is a number
|
|
396
|
-
*/
|
|
397
|
-
const isNumber = (value) => {
|
|
398
|
-
return typeof value === 'number';
|
|
399
|
-
};
|
|
400
|
-
|
|
401
|
-
/**
|
|
402
|
-
* Append glyph indices with given length
|
|
403
|
-
*
|
|
404
|
-
* Ex. appendIndices(3, [0, 1, 2, 2]) => [0, 1, 2, 2, 3, 3, 3]
|
|
405
|
-
*
|
|
406
|
-
* @param length - Length
|
|
407
|
-
* @param indices - Glyph indices
|
|
408
|
-
* @returns Extended glyph indices
|
|
409
|
-
*/
|
|
410
|
-
const appendIndices = (length, indices) => {
|
|
411
|
-
const lastIndex = last(indices);
|
|
412
|
-
const value = isNil(lastIndex) ? 0 : lastIndex + 1;
|
|
413
|
-
const newIndices = Array(length).fill(value);
|
|
414
|
-
return indices.concat(newIndices);
|
|
415
|
-
};
|
|
416
|
-
|
|
417
|
-
/**
|
|
418
|
-
* Get glyph for a given code point
|
|
419
|
-
*
|
|
420
|
-
* @param value - CodePoint
|
|
421
|
-
* @param font - Font
|
|
422
|
-
* @returns Glyph
|
|
423
|
-
* */
|
|
424
|
-
const fromCodePoint = (value, font) => {
|
|
425
|
-
if (typeof font === 'string')
|
|
426
|
-
return null;
|
|
427
|
-
return font && value ? font.glyphForCodePoint(value) : null;
|
|
428
|
-
};
|
|
429
|
-
|
|
430
|
-
/**
|
|
431
|
-
* Append glyph to run
|
|
432
|
-
*
|
|
433
|
-
* @param glyph - Glyph
|
|
434
|
-
* @param run - Run
|
|
435
|
-
* @returns Run with glyph
|
|
436
|
-
*/
|
|
437
|
-
const appendGlyph = (glyph, run) => {
|
|
438
|
-
const glyphLength = glyph.codePoints?.length || 0;
|
|
439
|
-
const end = run.end + glyphLength;
|
|
440
|
-
const glyphs = run.glyphs.concat(glyph);
|
|
441
|
-
const glyphIndices = appendIndices(glyphLength, run.glyphIndices);
|
|
442
|
-
if (!run.positions)
|
|
443
|
-
return Object.assign({}, run, { end, glyphs, glyphIndices });
|
|
444
|
-
const positions = run.positions.concat({
|
|
445
|
-
xAdvance: glyph.advanceWidth * scale(run),
|
|
446
|
-
yAdvance: 0,
|
|
447
|
-
xOffset: 0,
|
|
448
|
-
yOffset: 0,
|
|
449
|
-
});
|
|
450
|
-
return Object.assign({}, run, { end, glyphs, glyphIndices, positions });
|
|
451
|
-
};
|
|
452
|
-
/**
|
|
453
|
-
* Append glyph or code point to run
|
|
454
|
-
*
|
|
455
|
-
* @param value - Glyph or codePoint
|
|
456
|
-
* @param run - Run
|
|
457
|
-
* @returns Run with glyph
|
|
458
|
-
*/
|
|
459
|
-
const append$1 = (value, run) => {
|
|
460
|
-
if (!value)
|
|
461
|
-
return run;
|
|
462
|
-
const font = getFont(run);
|
|
463
|
-
const glyph = isNumber(value) ? fromCodePoint(value, font) : value;
|
|
464
|
-
return appendGlyph(glyph, run);
|
|
465
|
-
};
|
|
466
|
-
|
|
467
|
-
/**
|
|
468
|
-
* Get string from array of code points
|
|
469
|
-
*
|
|
470
|
-
* @param codePoints - Points
|
|
471
|
-
* @returns String
|
|
472
|
-
*/
|
|
473
|
-
const stringFromCodePoints = (codePoints) => {
|
|
474
|
-
return String.fromCodePoint(...(codePoints || []));
|
|
475
|
-
};
|
|
476
|
-
|
|
477
|
-
/**
|
|
478
|
-
* Append glyph into last run of attributed string
|
|
479
|
-
*
|
|
480
|
-
* @param glyph - Glyph or code point
|
|
481
|
-
* @param attributedString - Attributed string
|
|
482
|
-
* @returns Attributed string with new glyph
|
|
483
|
-
*/
|
|
484
|
-
const append = (glyph, attributedString) => {
|
|
485
|
-
const codePoints = typeof glyph === 'number' ? [glyph] : glyph?.codePoints;
|
|
486
|
-
const codePointsString = stringFromCodePoints(codePoints || []);
|
|
487
|
-
const string = attributedString.string + codePointsString;
|
|
488
|
-
const firstRuns = attributedString.runs.slice(0, -1);
|
|
489
|
-
const lastRun = last(attributedString.runs) || empty$1();
|
|
490
|
-
const runs = firstRuns.concat(append$1(glyph, lastRun));
|
|
491
|
-
return Object.assign({}, attributedString, { string, runs });
|
|
492
|
-
};
|
|
493
|
-
|
|
494
|
-
const ELLIPSIS_UNICODE = 8230;
|
|
495
|
-
const ELLIPSIS_STRING = String.fromCharCode(ELLIPSIS_UNICODE);
|
|
496
|
-
/**
|
|
497
|
-
* Get ellipsis codepoint. This may be different in standard and embedded fonts
|
|
498
|
-
*
|
|
499
|
-
* @param font
|
|
500
|
-
* @returns Ellipsis codepoint
|
|
501
|
-
*/
|
|
502
|
-
const getEllipsisCodePoint = (font) => {
|
|
503
|
-
if (!font.encode)
|
|
504
|
-
return ELLIPSIS_UNICODE;
|
|
505
|
-
const [codePoints] = font.encode(ELLIPSIS_STRING);
|
|
506
|
-
return parseInt(codePoints[0], 16);
|
|
507
|
-
};
|
|
508
|
-
/**
|
|
509
|
-
* Trucante block with ellipsis
|
|
510
|
-
*
|
|
511
|
-
* @param paragraph - Paragraph
|
|
512
|
-
* @returns Sliced paragraph
|
|
513
|
-
*/
|
|
514
|
-
const truncate = (paragraph) => {
|
|
515
|
-
const runs = last(paragraph)?.runs || [];
|
|
516
|
-
const font = last(runs)?.attributes?.font[0];
|
|
517
|
-
if (font) {
|
|
518
|
-
const index = paragraph.length - 1;
|
|
519
|
-
const codePoint = getEllipsisCodePoint(font);
|
|
520
|
-
const glyph = font.glyphForCodePoint(codePoint);
|
|
521
|
-
const lastBlock = append(glyph, trim(paragraph[index]));
|
|
522
|
-
return Object.assign([], paragraph, { [index]: lastBlock });
|
|
523
|
-
}
|
|
524
|
-
return paragraph;
|
|
525
|
-
};
|
|
526
|
-
|
|
527
|
-
/**
|
|
528
|
-
* Omit attribute from run
|
|
529
|
-
*
|
|
530
|
-
* @param value - Attribute key
|
|
531
|
-
* @param run - Run
|
|
532
|
-
* @returns Run without ommited attribute
|
|
533
|
-
*/
|
|
534
|
-
const omit = (value, run) => {
|
|
535
|
-
const attributes = Object.assign({}, run.attributes);
|
|
536
|
-
delete attributes[value];
|
|
537
|
-
return Object.assign({}, run, { attributes });
|
|
538
|
-
};
|
|
539
|
-
|
|
540
|
-
/**
|
|
541
|
-
* Get run ascent
|
|
542
|
-
*
|
|
543
|
-
* @param run - Run
|
|
544
|
-
* @returns Ascent
|
|
545
|
-
*/
|
|
546
|
-
const ascent$1 = (run) => {
|
|
547
|
-
const { font, attachment } = run.attributes;
|
|
548
|
-
const attachmentHeight = attachment?.height || 0;
|
|
549
|
-
const fontAscent = typeof font === 'string' ? 0 : font?.[0]?.ascent || 0;
|
|
550
|
-
return Math.max(attachmentHeight, fontAscent * scale(run));
|
|
551
|
-
};
|
|
552
|
-
|
|
553
|
-
/**
|
|
554
|
-
* Get run descent
|
|
555
|
-
*
|
|
556
|
-
* @param run - Run
|
|
557
|
-
* @returns Descent
|
|
558
|
-
*/
|
|
559
|
-
const descent = (run) => {
|
|
560
|
-
const font = run.attributes?.font;
|
|
561
|
-
const fontDescent = typeof font === 'string' ? 0 : font?.[0]?.descent || 0;
|
|
562
|
-
return scale(run) * fontDescent;
|
|
563
|
-
};
|
|
564
|
-
|
|
565
|
-
/**
|
|
566
|
-
* Get run lineGap
|
|
567
|
-
*
|
|
568
|
-
* @param run - Run
|
|
569
|
-
* @returns LineGap
|
|
570
|
-
*/
|
|
571
|
-
const lineGap = (run) => {
|
|
572
|
-
const font = run.attributes?.font;
|
|
573
|
-
const lineGap = typeof font === 'string' ? 0 : font?.[0]?.lineGap || 0;
|
|
574
|
-
return lineGap * scale(run);
|
|
575
|
-
};
|
|
576
|
-
|
|
577
|
-
/**
|
|
578
|
-
* Get run height
|
|
579
|
-
*
|
|
580
|
-
* @param run - Run
|
|
581
|
-
* @returns Height
|
|
582
|
-
*/
|
|
583
|
-
const height$1 = (run) => {
|
|
584
|
-
const lineHeight = run.attributes?.lineHeight;
|
|
585
|
-
return lineHeight || lineGap(run) + ascent$1(run) - descent(run);
|
|
586
|
-
};
|
|
587
|
-
|
|
588
|
-
/**
|
|
589
|
-
* Returns attributed string height
|
|
590
|
-
*
|
|
591
|
-
* @param attributedString - Attributed string
|
|
592
|
-
* @returns Height
|
|
593
|
-
*/
|
|
594
|
-
const height = (attributedString) => {
|
|
595
|
-
const reducer = (acc, run) => Math.max(acc, height$1(run));
|
|
596
|
-
return attributedString.runs.reduce(reducer, 0);
|
|
597
|
-
};
|
|
598
|
-
|
|
599
|
-
/**
|
|
600
|
-
* Checks if two rects intersect each other
|
|
601
|
-
*
|
|
602
|
-
* @param a - Rect A
|
|
603
|
-
* @param b - Rect B
|
|
604
|
-
* @returns Whether rects intersect
|
|
605
|
-
*/
|
|
606
|
-
const intersects = (a, b) => {
|
|
607
|
-
const x = Math.max(a.x, b.x);
|
|
608
|
-
const num1 = Math.min(a.x + a.width, b.x + b.width);
|
|
609
|
-
const y = Math.max(a.y, b.y);
|
|
610
|
-
const num2 = Math.min(a.y + a.height, b.y + b.height);
|
|
611
|
-
return num1 >= x && num2 >= y;
|
|
612
|
-
};
|
|
613
|
-
|
|
614
|
-
const getLineFragment = (lineRect, excludeRect) => {
|
|
615
|
-
if (!intersects(excludeRect, lineRect))
|
|
616
|
-
return [lineRect];
|
|
617
|
-
const eStart = excludeRect.x;
|
|
618
|
-
const eEnd = excludeRect.x + excludeRect.width;
|
|
619
|
-
const lStart = lineRect.x;
|
|
620
|
-
const lEnd = lineRect.x + lineRect.width;
|
|
621
|
-
const a = Object.assign({}, lineRect, { width: eStart - lStart });
|
|
622
|
-
const b = Object.assign({}, lineRect, { x: eEnd, width: lEnd - eEnd });
|
|
623
|
-
return [a, b].filter((r) => r.width > 0);
|
|
624
|
-
};
|
|
625
|
-
const getLineFragments = (rect, excludeRects) => {
|
|
626
|
-
let fragments = [rect];
|
|
627
|
-
for (let i = 0; i < excludeRects.length; i += 1) {
|
|
628
|
-
const excludeRect = excludeRects[i];
|
|
629
|
-
fragments = fragments.reduce((acc, fragment) => {
|
|
630
|
-
const pieces = getLineFragment(fragment, excludeRect);
|
|
631
|
-
return acc.concat(pieces);
|
|
632
|
-
}, []);
|
|
633
|
-
}
|
|
634
|
-
return fragments;
|
|
635
|
-
};
|
|
636
|
-
const generateLineRects = (container, height) => {
|
|
637
|
-
const { excludeRects, ...rect } = container;
|
|
638
|
-
if (!excludeRects)
|
|
639
|
-
return [rect];
|
|
640
|
-
const lineRects = [];
|
|
641
|
-
const maxY = Math.max(...excludeRects.map((r) => r.y + r.height));
|
|
642
|
-
let currentRect = rect;
|
|
643
|
-
while (currentRect.y < maxY) {
|
|
644
|
-
const [lineRect, rest] = partition(currentRect, height);
|
|
645
|
-
const lineRectFragments = getLineFragments(lineRect, excludeRects);
|
|
646
|
-
currentRect = rest;
|
|
647
|
-
lineRects.push(...lineRectFragments);
|
|
648
|
-
}
|
|
649
|
-
return [...lineRects, currentRect];
|
|
650
|
-
};
|
|
651
|
-
|
|
652
|
-
const ATTACHMENT_CODE$1 = '\ufffc'; // 65532
|
|
653
|
-
/**
|
|
654
|
-
* Remove attachment attribute if no char present
|
|
655
|
-
*
|
|
656
|
-
* @param line - Line
|
|
657
|
-
* @returns Line
|
|
658
|
-
*/
|
|
659
|
-
const purgeAttachments = (line) => {
|
|
660
|
-
const shouldPurge = !line.string.includes(ATTACHMENT_CODE$1);
|
|
661
|
-
if (!shouldPurge)
|
|
662
|
-
return line;
|
|
663
|
-
const runs = line.runs.map((run) => omit('attachment', run));
|
|
664
|
-
return Object.assign({}, line, { runs });
|
|
665
|
-
};
|
|
666
|
-
/**
|
|
667
|
-
* Layout paragraphs inside rectangle
|
|
668
|
-
*
|
|
669
|
-
* @param rects - Rects
|
|
670
|
-
* @param lines - Attributed strings
|
|
671
|
-
* @param indent
|
|
672
|
-
* @returns layout blocks
|
|
673
|
-
*/
|
|
674
|
-
const layoutLines = (rects, lines, indent) => {
|
|
675
|
-
let rect = rects.shift();
|
|
676
|
-
let currentY = rect.y;
|
|
677
|
-
return lines.map((line, i) => {
|
|
678
|
-
const lineIndent = i === 0 ? indent : 0;
|
|
679
|
-
const style = line.runs?.[0]?.attributes || {};
|
|
680
|
-
const height$1 = Math.max(height(line), style.lineHeight);
|
|
681
|
-
if (currentY + height$1 > rect.y + rect.height && rects.length > 0) {
|
|
682
|
-
rect = rects.shift();
|
|
683
|
-
currentY = rect.y;
|
|
684
|
-
}
|
|
685
|
-
const newLine = {
|
|
686
|
-
string: line.string,
|
|
687
|
-
runs: line.runs,
|
|
688
|
-
box: {
|
|
689
|
-
x: rect.x + lineIndent,
|
|
690
|
-
y: currentY,
|
|
691
|
-
width: rect.width - lineIndent,
|
|
692
|
-
height: height$1,
|
|
693
|
-
},
|
|
694
|
-
};
|
|
695
|
-
currentY += height$1;
|
|
696
|
-
return purgeAttachments(newLine);
|
|
697
|
-
});
|
|
698
|
-
};
|
|
699
|
-
/**
|
|
700
|
-
* Performs line breaking and layout
|
|
701
|
-
*
|
|
702
|
-
* @param engines - Engines
|
|
703
|
-
* @param options - Layout options
|
|
704
|
-
*/
|
|
705
|
-
const layoutParagraph = (engines, options = {}) => {
|
|
706
|
-
/**
|
|
707
|
-
* @param container - Container
|
|
708
|
-
* @param paragraph - Attributed string
|
|
709
|
-
* @returns Layout block
|
|
710
|
-
*/
|
|
711
|
-
return (container, paragraph) => {
|
|
712
|
-
const height$1 = height(paragraph);
|
|
713
|
-
const indent = paragraph.runs?.[0]?.attributes?.indent || 0;
|
|
714
|
-
const rects = generateLineRects(container, height$1);
|
|
715
|
-
const availableWidths = rects.map((r) => r.width);
|
|
716
|
-
availableWidths.unshift(availableWidths[0] - indent);
|
|
717
|
-
const lines = engines.linebreaker(options)(paragraph, availableWidths);
|
|
718
|
-
return layoutLines(rects, lines, indent);
|
|
719
|
-
};
|
|
720
|
-
};
|
|
721
|
-
|
|
722
|
-
/**
|
|
723
|
-
* Slice block at given height
|
|
724
|
-
*
|
|
725
|
-
* @param height - Height
|
|
726
|
-
* @param paragraph - Paragraph
|
|
727
|
-
* @returns Sliced paragraph
|
|
728
|
-
*/
|
|
729
|
-
const sliceAtHeight = (height, paragraph) => {
|
|
730
|
-
const newBlock = [];
|
|
731
|
-
let counter = 0;
|
|
732
|
-
for (let i = 0; i < paragraph.length; i += 1) {
|
|
733
|
-
const line = paragraph[i];
|
|
734
|
-
counter += line.box.height;
|
|
735
|
-
if (counter < height) {
|
|
736
|
-
newBlock.push(line);
|
|
737
|
-
}
|
|
738
|
-
else {
|
|
739
|
-
break;
|
|
740
|
-
}
|
|
741
|
-
}
|
|
742
|
-
return newBlock;
|
|
743
|
-
};
|
|
744
|
-
|
|
745
|
-
/**
|
|
746
|
-
* Layout paragraphs inside container until it does not
|
|
747
|
-
* fit anymore, performing line wrapping in the process.
|
|
748
|
-
*
|
|
749
|
-
* @param engines - Engines
|
|
750
|
-
* @param options - Layout options
|
|
751
|
-
* @param container - Container
|
|
752
|
-
*/
|
|
753
|
-
const typesetter = (engines, options, container) => {
|
|
754
|
-
/**
|
|
755
|
-
* @param attributedStrings - Attributed strings (paragraphs)
|
|
756
|
-
* @returns Paragraph blocks
|
|
757
|
-
*/
|
|
758
|
-
return (attributedStrings) => {
|
|
759
|
-
const result = [];
|
|
760
|
-
const paragraphs = [...attributedStrings];
|
|
761
|
-
const layout = layoutParagraph(engines, options);
|
|
762
|
-
const maxLines = isNil(container.maxLines) ? Infinity : container.maxLines;
|
|
763
|
-
const truncateEllipsis = container.truncateMode === 'ellipsis';
|
|
764
|
-
let linesCount = maxLines;
|
|
765
|
-
let paragraphRect = copy(container);
|
|
766
|
-
let nextParagraph = paragraphs.shift();
|
|
767
|
-
while (linesCount > 0 && nextParagraph) {
|
|
768
|
-
const paragraph = layout(paragraphRect, nextParagraph);
|
|
769
|
-
const slicedBlock = paragraph.slice(0, linesCount);
|
|
770
|
-
const linesHeight = height$2(slicedBlock);
|
|
771
|
-
const shouldTruncate = truncateEllipsis && paragraph.length !== slicedBlock.length;
|
|
772
|
-
linesCount -= slicedBlock.length;
|
|
773
|
-
if (paragraphRect.height >= linesHeight) {
|
|
774
|
-
result.push(shouldTruncate ? truncate(slicedBlock) : slicedBlock);
|
|
775
|
-
paragraphRect = crop(linesHeight, paragraphRect);
|
|
776
|
-
nextParagraph = paragraphs.shift();
|
|
777
|
-
}
|
|
778
|
-
else {
|
|
779
|
-
result.push(truncate(sliceAtHeight(paragraphRect.height, slicedBlock)));
|
|
780
|
-
break;
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
return result;
|
|
784
|
-
};
|
|
785
|
-
};
|
|
786
|
-
|
|
787
|
-
/**
|
|
788
|
-
* Get attributed string start value
|
|
789
|
-
*
|
|
790
|
-
* @param attributedString - Attributed string
|
|
791
|
-
* @returns Start
|
|
792
|
-
*/
|
|
793
|
-
const start = (attributedString) => {
|
|
794
|
-
const { runs } = attributedString;
|
|
795
|
-
return runs.length === 0 ? 0 : runs[0].start;
|
|
796
|
-
};
|
|
797
|
-
|
|
798
|
-
/**
|
|
799
|
-
* Get attributed string end value
|
|
800
|
-
*
|
|
801
|
-
* @param attributedString - Attributed string
|
|
802
|
-
* @returns End
|
|
803
|
-
*/
|
|
804
|
-
const end = (attributedString) => {
|
|
805
|
-
const { runs } = attributedString;
|
|
806
|
-
return runs.length === 0 ? 0 : last(runs).end;
|
|
807
|
-
};
|
|
808
|
-
|
|
809
|
-
/**
|
|
810
|
-
* Get attributed string length
|
|
811
|
-
*
|
|
812
|
-
* @param attributedString - Attributed string
|
|
813
|
-
* @returns End
|
|
814
|
-
*/
|
|
815
|
-
const length$1 = (attributedString) => {
|
|
816
|
-
return end(attributedString) - start(attributedString);
|
|
817
|
-
};
|
|
818
|
-
|
|
819
|
-
const bidi$2 = bidiFactory();
|
|
820
|
-
const getBidiLevels$1 = (runs) => {
|
|
821
|
-
return runs.reduce((acc, run) => {
|
|
822
|
-
const length = run.end - run.start;
|
|
823
|
-
const levels = repeat(run.attributes.bidiLevel, length);
|
|
824
|
-
return acc.concat(levels);
|
|
825
|
-
}, []);
|
|
826
|
-
};
|
|
827
|
-
const getReorderedIndices = (string, segments) => {
|
|
828
|
-
// Fill an array with indices
|
|
829
|
-
const indices = [];
|
|
830
|
-
for (let i = 0; i < string.length; i += 1) {
|
|
831
|
-
indices[i] = i;
|
|
832
|
-
}
|
|
833
|
-
// Reverse each segment in order
|
|
834
|
-
segments.forEach(([start, end]) => {
|
|
835
|
-
const slice = indices.slice(start, end + 1);
|
|
836
|
-
for (let i = slice.length - 1; i >= 0; i -= 1) {
|
|
837
|
-
indices[end - i] = slice[i];
|
|
838
|
-
}
|
|
839
|
-
});
|
|
840
|
-
return indices;
|
|
841
|
-
};
|
|
842
|
-
const getItemAtIndex = (runs, objectName, index) => {
|
|
843
|
-
for (let i = 0; i < runs.length; i += 1) {
|
|
844
|
-
const run = runs[i];
|
|
845
|
-
const updatedIndex = run.glyphIndices[index - run.start];
|
|
846
|
-
if (index >= run.start && index < run.end) {
|
|
847
|
-
return run[objectName][updatedIndex];
|
|
848
|
-
}
|
|
849
|
-
}
|
|
850
|
-
throw new Error(`index ${index} out of range`);
|
|
851
|
-
};
|
|
852
|
-
const reorderLine = (line) => {
|
|
853
|
-
const levels = getBidiLevels$1(line.runs);
|
|
854
|
-
const direction = line.runs[0]?.attributes.direction;
|
|
855
|
-
const level = direction === 'rtl' ? 1 : 0;
|
|
856
|
-
const end = length$1(line) - 1;
|
|
857
|
-
const paragraphs = [{ start: 0, end, level }];
|
|
858
|
-
const embeddingLevels = { paragraphs, levels };
|
|
859
|
-
const segments = bidi$2.getReorderSegments(line.string, embeddingLevels);
|
|
860
|
-
// No need for bidi reordering
|
|
861
|
-
if (segments.length === 0)
|
|
862
|
-
return line;
|
|
863
|
-
const indices = getReorderedIndices(line.string, segments);
|
|
864
|
-
const updatedString = bidi$2.getReorderedString(line.string, embeddingLevels);
|
|
865
|
-
const updatedRuns = line.runs.map((run) => {
|
|
866
|
-
const selectedIndices = indices.slice(run.start, run.end);
|
|
867
|
-
const updatedGlyphs = [];
|
|
868
|
-
const updatedPositions = [];
|
|
869
|
-
const addedGlyphs = new Set();
|
|
870
|
-
for (let i = 0; i < selectedIndices.length; i += 1) {
|
|
871
|
-
const index = selectedIndices[i];
|
|
872
|
-
const glyph = getItemAtIndex(line.runs, 'glyphs', index);
|
|
873
|
-
if (addedGlyphs.has(glyph.id))
|
|
874
|
-
continue;
|
|
875
|
-
updatedGlyphs.push(glyph);
|
|
876
|
-
updatedPositions.push(getItemAtIndex(line.runs, 'positions', index));
|
|
877
|
-
if (glyph.isLigature) {
|
|
878
|
-
addedGlyphs.add(glyph.id);
|
|
879
|
-
}
|
|
880
|
-
}
|
|
881
|
-
return {
|
|
882
|
-
...run,
|
|
883
|
-
glyphs: updatedGlyphs,
|
|
884
|
-
positions: updatedPositions,
|
|
885
|
-
};
|
|
886
|
-
});
|
|
887
|
-
return {
|
|
888
|
-
box: line.box,
|
|
889
|
-
runs: updatedRuns,
|
|
890
|
-
string: updatedString,
|
|
891
|
-
};
|
|
892
|
-
};
|
|
893
|
-
const reorderParagraph = (paragraph) => paragraph.map(reorderLine);
|
|
894
|
-
/**
|
|
895
|
-
* Perform bidi reordering
|
|
896
|
-
*
|
|
897
|
-
* @returns Reordered paragraphs
|
|
898
|
-
*/
|
|
899
|
-
const bidiReordering = () => {
|
|
900
|
-
/**
|
|
901
|
-
* @param paragraphs - Paragraphs
|
|
902
|
-
* @returns Reordered paragraphs
|
|
903
|
-
*/
|
|
904
|
-
return (paragraphs) => paragraphs.map(reorderParagraph);
|
|
905
|
-
};
|
|
906
|
-
|
|
907
|
-
const DUMMY_CODEPOINT = 123;
|
|
908
|
-
/**
|
|
909
|
-
* Resolve string indices based on glyphs code points
|
|
910
|
-
*
|
|
911
|
-
* @param glyphs
|
|
912
|
-
* @returns Glyph indices
|
|
913
|
-
*/
|
|
914
|
-
const resolve = (glyphs = []) => {
|
|
915
|
-
return glyphs.reduce((acc, glyph) => {
|
|
916
|
-
const codePoints = glyph?.codePoints || [DUMMY_CODEPOINT];
|
|
917
|
-
if (acc.length === 0)
|
|
918
|
-
return codePoints.map(() => 0);
|
|
919
|
-
const last = acc[acc.length - 1];
|
|
920
|
-
const next = codePoints.map(() => last + 1);
|
|
921
|
-
return [...acc, ...next];
|
|
922
|
-
}, []);
|
|
923
|
-
};
|
|
924
|
-
|
|
925
|
-
const getCharacterSpacing = (run) => {
|
|
926
|
-
return run.attributes?.characterSpacing || 0;
|
|
927
|
-
};
|
|
928
|
-
/**
|
|
929
|
-
* Scale run positions
|
|
930
|
-
*
|
|
931
|
-
* @param run
|
|
932
|
-
* @param positions
|
|
933
|
-
* @returns Scaled positions
|
|
934
|
-
*/
|
|
935
|
-
const scalePositions = (run, positions) => {
|
|
936
|
-
const runScale = scale(run);
|
|
937
|
-
const characterSpacing = getCharacterSpacing(run);
|
|
938
|
-
return positions.map((position, i) => {
|
|
939
|
-
const isLast = i === positions.length;
|
|
940
|
-
const xSpacing = isLast ? 0 : characterSpacing;
|
|
941
|
-
return Object.assign({}, position, {
|
|
942
|
-
xAdvance: position.xAdvance * runScale + xSpacing,
|
|
943
|
-
yAdvance: position.yAdvance * runScale,
|
|
944
|
-
xOffset: position.xOffset * runScale,
|
|
945
|
-
yOffset: position.yOffset * runScale,
|
|
946
|
-
});
|
|
947
|
-
});
|
|
948
|
-
};
|
|
949
|
-
/**
|
|
950
|
-
* Create glyph run
|
|
951
|
-
*
|
|
952
|
-
* @param string string
|
|
953
|
-
*/
|
|
954
|
-
const layoutRun = (string) => {
|
|
955
|
-
/**
|
|
956
|
-
* @param run - Run
|
|
957
|
-
* @returns Glyph run
|
|
958
|
-
*/
|
|
959
|
-
return (run) => {
|
|
960
|
-
const { start, end, attributes = {} } = run;
|
|
961
|
-
const { font } = attributes;
|
|
962
|
-
if (!font)
|
|
963
|
-
return { ...run, glyphs: [], glyphIndices: [], positions: [] };
|
|
964
|
-
const runString = string.slice(start, end);
|
|
965
|
-
if (typeof font === 'string')
|
|
966
|
-
throw new Error('Invalid font');
|
|
967
|
-
// passing LTR To force fontkit to not reverse the string
|
|
968
|
-
const glyphRun = font[0].layout(runString, undefined, undefined, undefined, 'ltr');
|
|
969
|
-
const positions = scalePositions(run, glyphRun.positions);
|
|
970
|
-
const glyphIndices = resolve(glyphRun.glyphs);
|
|
971
|
-
const result = {
|
|
972
|
-
...run,
|
|
973
|
-
positions,
|
|
974
|
-
glyphIndices,
|
|
975
|
-
glyphs: glyphRun.glyphs,
|
|
976
|
-
};
|
|
977
|
-
return result;
|
|
978
|
-
};
|
|
979
|
-
};
|
|
980
|
-
/**
|
|
981
|
-
* Generate glyphs for single attributed string
|
|
982
|
-
*/
|
|
983
|
-
const generateGlyphs = () => {
|
|
984
|
-
/**
|
|
985
|
-
* @param attributedString - Attributed string
|
|
986
|
-
* @returns Attributed string with glyphs
|
|
987
|
-
*/
|
|
988
|
-
return (attributedString) => {
|
|
989
|
-
const runs = attributedString.runs.map(layoutRun(attributedString.string));
|
|
990
|
-
const res = Object.assign({}, attributedString, { runs });
|
|
991
|
-
return res;
|
|
992
|
-
};
|
|
993
|
-
};
|
|
994
|
-
|
|
995
|
-
/**
|
|
996
|
-
* Resolves yOffset for run
|
|
997
|
-
*
|
|
998
|
-
* @param run - Run
|
|
999
|
-
* @returns Run
|
|
1000
|
-
*/
|
|
1001
|
-
const resolveRunYOffset = (run) => {
|
|
1002
|
-
if (!run.positions)
|
|
1003
|
-
return run;
|
|
1004
|
-
const unitsPerEm = run.attributes?.font?.[0]?.unitsPerEm || 0;
|
|
1005
|
-
const yOffset = (run.attributes?.yOffset || 0) * unitsPerEm;
|
|
1006
|
-
const positions = run.positions.map((p) => Object.assign({}, p, { yOffset }));
|
|
1007
|
-
return Object.assign({}, run, { positions });
|
|
1008
|
-
};
|
|
1009
|
-
/**
|
|
1010
|
-
* Resolves yOffset for multiple paragraphs
|
|
1011
|
-
*/
|
|
1012
|
-
const resolveYOffset = () => {
|
|
1013
|
-
/**
|
|
1014
|
-
* @param attributedString - Attributed string
|
|
1015
|
-
* @returns Attributed string
|
|
1016
|
-
*/
|
|
1017
|
-
return (attributedString) => {
|
|
1018
|
-
const runs = attributedString.runs.map(resolveRunYOffset);
|
|
1019
|
-
const res = Object.assign({}, attributedString, { runs });
|
|
1020
|
-
return res;
|
|
1021
|
-
};
|
|
1022
|
-
};
|
|
1023
|
-
|
|
1024
|
-
/**
|
|
1025
|
-
* Sort runs in ascending order
|
|
1026
|
-
*
|
|
1027
|
-
* @param runs
|
|
1028
|
-
* @returns Sorted runs
|
|
1029
|
-
*/
|
|
1030
|
-
const sort = (runs) => {
|
|
1031
|
-
return runs.sort((a, b) => a.start - b.start || a.end - b.end);
|
|
1032
|
-
};
|
|
1033
|
-
|
|
1034
|
-
/**
|
|
1035
|
-
* Is run empty (start === end)
|
|
1036
|
-
*
|
|
1037
|
-
* @param run - Run
|
|
1038
|
-
* @returns Is run empty
|
|
1039
|
-
*/
|
|
1040
|
-
const isEmpty = (run) => {
|
|
1041
|
-
return run.start === run.end;
|
|
1042
|
-
};
|
|
1043
|
-
|
|
1044
|
-
/**
|
|
1045
|
-
* Sort points in ascending order
|
|
1046
|
-
* @param a - First point
|
|
1047
|
-
* @param b - Second point
|
|
1048
|
-
* @returns Sort order
|
|
1049
|
-
*/
|
|
1050
|
-
const sortPoints = (a, b) => {
|
|
1051
|
-
return a[1] - b[1] || a[3] - b[3];
|
|
1052
|
-
};
|
|
1053
|
-
/**
|
|
1054
|
-
* @param runs
|
|
1055
|
-
* @returns Points
|
|
1056
|
-
*/
|
|
1057
|
-
const generatePoints = (runs) => {
|
|
1058
|
-
const result = runs.reduce((acc, run, i) => {
|
|
1059
|
-
return acc.concat([
|
|
1060
|
-
['start', run.start, run.attributes, i],
|
|
1061
|
-
['end', run.end, run.attributes, i],
|
|
1062
|
-
]);
|
|
1063
|
-
}, []);
|
|
1064
|
-
return result.sort(sortPoints);
|
|
1065
|
-
};
|
|
1066
|
-
/**
|
|
1067
|
-
* @param runs
|
|
1068
|
-
* @returns Merged runs
|
|
1069
|
-
*/
|
|
1070
|
-
const mergeRuns = (runs) => {
|
|
1071
|
-
return runs.reduce((acc, run) => {
|
|
1072
|
-
const attributes = Object.assign({}, acc.attributes, run.attributes);
|
|
1073
|
-
return Object.assign({}, run, { attributes });
|
|
1074
|
-
}, {});
|
|
1075
|
-
};
|
|
1076
|
-
/**
|
|
1077
|
-
* @param runs
|
|
1078
|
-
* @returns Grouped runs
|
|
1079
|
-
*/
|
|
1080
|
-
const groupEmptyRuns = (runs) => {
|
|
1081
|
-
const groups = runs.reduce((acc, run) => {
|
|
1082
|
-
if (!acc[run.start])
|
|
1083
|
-
acc[run.start] = [];
|
|
1084
|
-
acc[run.start].push(run);
|
|
1085
|
-
return acc;
|
|
1086
|
-
}, []);
|
|
1087
|
-
return Object.values(groups);
|
|
1088
|
-
};
|
|
1089
|
-
/**
|
|
1090
|
-
* @param runs
|
|
1091
|
-
* @returns Flattened runs
|
|
1092
|
-
*/
|
|
1093
|
-
const flattenEmptyRuns = (runs) => {
|
|
1094
|
-
return groupEmptyRuns(runs).map(mergeRuns);
|
|
1095
|
-
};
|
|
1096
|
-
/**
|
|
1097
|
-
* @param runs
|
|
1098
|
-
* @returns Flattened runs
|
|
1099
|
-
*/
|
|
1100
|
-
const flattenRegularRuns = (runs) => {
|
|
1101
|
-
const res = [];
|
|
1102
|
-
const points = generatePoints(runs);
|
|
1103
|
-
let start = -1;
|
|
1104
|
-
let attrs = {};
|
|
1105
|
-
const stack = [];
|
|
1106
|
-
for (let i = 0; i < points.length; i += 1) {
|
|
1107
|
-
const [type, offset, attributes] = points[i];
|
|
1108
|
-
if (start !== -1 && start < offset) {
|
|
1109
|
-
res.push({
|
|
1110
|
-
start,
|
|
1111
|
-
end: offset,
|
|
1112
|
-
attributes: attrs,
|
|
1113
|
-
glyphIndices: [],
|
|
1114
|
-
glyphs: [],
|
|
1115
|
-
positions: [],
|
|
1116
|
-
});
|
|
1117
|
-
}
|
|
1118
|
-
if (type === 'start') {
|
|
1119
|
-
stack.push(attributes);
|
|
1120
|
-
attrs = Object.assign({}, attrs, attributes);
|
|
1121
|
-
}
|
|
1122
|
-
else {
|
|
1123
|
-
attrs = {};
|
|
1124
|
-
for (let j = 0; j < stack.length; j += 1) {
|
|
1125
|
-
if (stack[j] === attributes) {
|
|
1126
|
-
stack.splice(j--, 1);
|
|
1127
|
-
}
|
|
1128
|
-
else {
|
|
1129
|
-
attrs = Object.assign({}, attrs, stack[j]);
|
|
1130
|
-
}
|
|
1131
|
-
}
|
|
1132
|
-
}
|
|
1133
|
-
start = offset;
|
|
1134
|
-
}
|
|
1135
|
-
return res;
|
|
1136
|
-
};
|
|
1137
|
-
/**
|
|
1138
|
-
* Flatten many runs
|
|
1139
|
-
*
|
|
1140
|
-
* @param runs
|
|
1141
|
-
* @returns Flattened runs
|
|
1142
|
-
*/
|
|
1143
|
-
const flatten = (runs = []) => {
|
|
1144
|
-
const emptyRuns = flattenEmptyRuns(runs.filter((run) => isEmpty(run)));
|
|
1145
|
-
const regularRuns = flattenRegularRuns(runs.filter((run) => !isEmpty(run)));
|
|
1146
|
-
return sort(emptyRuns.concat(regularRuns));
|
|
1147
|
-
};
|
|
1148
|
-
|
|
1149
|
-
/**
|
|
1150
|
-
* Returns empty attributed string
|
|
1151
|
-
*
|
|
1152
|
-
* @returns Empty attributed string
|
|
1153
|
-
*/
|
|
1154
|
-
const empty = () => ({ string: '', runs: [] });
|
|
1155
|
-
|
|
1156
|
-
/**
|
|
1157
|
-
*
|
|
1158
|
-
* @param attributedString
|
|
1159
|
-
* @returns Attributed string without font
|
|
1160
|
-
*/
|
|
1161
|
-
const omitFont = (attributedString) => {
|
|
1162
|
-
const runs = attributedString.runs.map((run) => omit('font', run));
|
|
1163
|
-
return Object.assign({}, attributedString, { runs });
|
|
1164
|
-
};
|
|
1165
|
-
/**
|
|
1166
|
-
* Performs font substitution and script itemization on attributed string
|
|
1167
|
-
*
|
|
1168
|
-
* @param engines - engines
|
|
1169
|
-
*/
|
|
1170
|
-
const preprocessRuns = (engines) => {
|
|
1171
|
-
/**
|
|
1172
|
-
* @param attributedString - Attributed string
|
|
1173
|
-
* @returns Processed attributed string
|
|
1174
|
-
*/
|
|
1175
|
-
return (attributedString) => {
|
|
1176
|
-
if (isNil(attributedString))
|
|
1177
|
-
return empty();
|
|
1178
|
-
const { string } = attributedString;
|
|
1179
|
-
const { fontSubstitution, scriptItemizer, bidi } = engines;
|
|
1180
|
-
const { runs: omittedFontRuns } = omitFont(attributedString);
|
|
1181
|
-
const { runs: itemizationRuns } = scriptItemizer()(attributedString);
|
|
1182
|
-
const { runs: substitutedRuns } = fontSubstitution()(attributedString);
|
|
1183
|
-
const { runs: bidiRuns } = bidi()(attributedString);
|
|
1184
|
-
const runs = bidiRuns
|
|
1185
|
-
.concat(substitutedRuns)
|
|
1186
|
-
.concat(itemizationRuns)
|
|
1187
|
-
.concat(omittedFontRuns);
|
|
1188
|
-
return { string, runs: flatten(runs) };
|
|
1189
|
-
};
|
|
1190
|
-
};
|
|
1191
|
-
|
|
1192
|
-
/**
|
|
1193
|
-
* Breaks attributed string into paragraphs
|
|
1194
|
-
*/
|
|
1195
|
-
const splitParagraphs = () => {
|
|
1196
|
-
/**
|
|
1197
|
-
* @param attributedString - Attributed string
|
|
1198
|
-
* @returns Paragraphs attributed strings
|
|
1199
|
-
*/
|
|
1200
|
-
return (attributedString) => {
|
|
1201
|
-
const paragraphs = [];
|
|
1202
|
-
let start = 0;
|
|
1203
|
-
let breakPoint = attributedString.string.indexOf('\n') + 1;
|
|
1204
|
-
while (breakPoint > 0) {
|
|
1205
|
-
paragraphs.push(slice(start, breakPoint, attributedString));
|
|
1206
|
-
start = breakPoint;
|
|
1207
|
-
breakPoint = attributedString.string.indexOf('\n', breakPoint) + 1;
|
|
1208
|
-
}
|
|
1209
|
-
if (start === 0) {
|
|
1210
|
-
paragraphs.push(attributedString);
|
|
1211
|
-
}
|
|
1212
|
-
else if (start < attributedString.string.length) {
|
|
1213
|
-
paragraphs.push(slice(start, length$1(attributedString), attributedString));
|
|
1214
|
-
}
|
|
1215
|
-
return paragraphs;
|
|
1216
|
-
};
|
|
1217
|
-
};
|
|
1218
|
-
|
|
1219
|
-
/**
|
|
1220
|
-
* Return positions advance width
|
|
1221
|
-
*
|
|
1222
|
-
* @param positions - Positions
|
|
1223
|
-
* @returns {number} advance width
|
|
1224
|
-
*/
|
|
1225
|
-
const advanceWidth$2 = (positions) => {
|
|
1226
|
-
return positions.reduce((acc, pos) => acc + (pos.xAdvance || 0), 0);
|
|
1227
|
-
};
|
|
1228
|
-
|
|
1229
|
-
/**
|
|
1230
|
-
* Return run advance width
|
|
1231
|
-
*
|
|
1232
|
-
* @param run - Run
|
|
1233
|
-
* @returns Advance width
|
|
1234
|
-
*/
|
|
1235
|
-
const advanceWidth$1 = (run) => {
|
|
1236
|
-
return advanceWidth$2(run.positions || []);
|
|
1237
|
-
};
|
|
1238
|
-
|
|
1239
|
-
/**
|
|
1240
|
-
* Returns attributed string advancewidth
|
|
1241
|
-
*
|
|
1242
|
-
* @param attributedString - Attributed string
|
|
1243
|
-
* @returns Advance width
|
|
1244
|
-
*/
|
|
1245
|
-
const advanceWidth = (attributedString) => {
|
|
1246
|
-
const reducer = (acc, run) => acc + advanceWidth$1(run);
|
|
1247
|
-
return attributedString.runs.reduce(reducer, 0);
|
|
1248
|
-
};
|
|
1249
|
-
|
|
1250
|
-
const WHITE_SPACES_CODE = 32;
|
|
1251
|
-
/**
|
|
1252
|
-
* Check if glyph is white space
|
|
1253
|
-
*
|
|
1254
|
-
* @param glyph - Glyph
|
|
1255
|
-
* @returns Whether glyph is white space
|
|
1256
|
-
* */
|
|
1257
|
-
const isWhiteSpace = (glyph) => {
|
|
1258
|
-
const codePoints = glyph?.codePoints || [];
|
|
1259
|
-
return codePoints.includes(WHITE_SPACES_CODE);
|
|
1260
|
-
};
|
|
1261
|
-
|
|
1262
|
-
/**
|
|
1263
|
-
* Get white space leading positions
|
|
1264
|
-
*
|
|
1265
|
-
* @param run - Run
|
|
1266
|
-
* @returns White space leading positions
|
|
1267
|
-
*/
|
|
1268
|
-
const leadingPositions = (run) => {
|
|
1269
|
-
const glyphs = run.glyphs || [];
|
|
1270
|
-
const positions = run.positions || [];
|
|
1271
|
-
const leadingWhitespaces = glyphs.findIndex((g) => !isWhiteSpace(g));
|
|
1272
|
-
return positions.slice(0, leadingWhitespaces);
|
|
1273
|
-
};
|
|
1274
|
-
/**
|
|
1275
|
-
* Get run leading white space offset
|
|
1276
|
-
*
|
|
1277
|
-
* @param run - Run
|
|
1278
|
-
* @returns Leading white space offset
|
|
1279
|
-
*/
|
|
1280
|
-
const leadingOffset$1 = (run) => {
|
|
1281
|
-
const positions = leadingPositions(run);
|
|
1282
|
-
return positions.reduce((acc, pos) => acc + (pos.xAdvance || 0), 0);
|
|
1283
|
-
};
|
|
1284
|
-
|
|
1285
|
-
/**
|
|
1286
|
-
* Get attributed string leading white space offset
|
|
1287
|
-
*
|
|
1288
|
-
* @param attributedString - Attributed string
|
|
1289
|
-
* @returns Leading white space offset
|
|
1290
|
-
*/
|
|
1291
|
-
const leadingOffset = (attributedString) => {
|
|
1292
|
-
const runs = attributedString.runs || [];
|
|
1293
|
-
return leadingOffset$1(runs[0]);
|
|
1294
|
-
};
|
|
1295
|
-
|
|
1296
|
-
/**
|
|
1297
|
-
* Get white space trailing positions
|
|
1298
|
-
*
|
|
1299
|
-
* @param run run
|
|
1300
|
-
* @returns White space trailing positions
|
|
1301
|
-
*/
|
|
1302
|
-
const trailingPositions = (run) => {
|
|
1303
|
-
const glyphs = reverse(run.glyphs || []);
|
|
1304
|
-
const positions = reverse(run.positions || []);
|
|
1305
|
-
const leadingWhitespaces = glyphs.findIndex((g) => !isWhiteSpace(g));
|
|
1306
|
-
return positions.slice(0, leadingWhitespaces);
|
|
1307
|
-
};
|
|
1308
|
-
/**
|
|
1309
|
-
* Get run trailing white space offset
|
|
1310
|
-
*
|
|
1311
|
-
* @param run - Run
|
|
1312
|
-
* @returns Trailing white space offset
|
|
1313
|
-
*/
|
|
1314
|
-
const trailingOffset$1 = (run) => {
|
|
1315
|
-
const positions = trailingPositions(run);
|
|
1316
|
-
return positions.reduce((acc, pos) => acc + (pos.xAdvance || 0), 0);
|
|
1317
|
-
};
|
|
1318
|
-
|
|
1319
|
-
/**
|
|
1320
|
-
* Get attributed string trailing white space offset
|
|
1321
|
-
*
|
|
1322
|
-
* @param attributedString - Attributed string
|
|
1323
|
-
* @returns Trailing white space offset
|
|
1324
|
-
*/
|
|
1325
|
-
const trailingOffset = (attributedString) => {
|
|
1326
|
-
const runs = attributedString.runs || [];
|
|
1327
|
-
return trailingOffset$1(last(runs));
|
|
1328
|
-
};
|
|
1329
|
-
|
|
1330
|
-
/**
|
|
1331
|
-
* Drop last char of run
|
|
1332
|
-
*
|
|
1333
|
-
* @param run - Run
|
|
1334
|
-
* @returns Run without last char
|
|
1335
|
-
*/
|
|
1336
|
-
const dropLast$1 = (run) => {
|
|
1337
|
-
return slice$1(0, run.end - run.start - 1, run);
|
|
1338
|
-
};
|
|
1339
|
-
|
|
1340
|
-
/**
|
|
1341
|
-
* Drop last glyph
|
|
1342
|
-
*
|
|
1343
|
-
* @param attributedString - Attributed string
|
|
1344
|
-
* @returns Attributed string with new glyph
|
|
1345
|
-
*/
|
|
1346
|
-
const dropLast = (attributedString) => {
|
|
1347
|
-
const string = dropLast$2(attributedString.string);
|
|
1348
|
-
const runs = adjust(-1, dropLast$1, attributedString.runs);
|
|
1349
|
-
return Object.assign({}, attributedString, { string, runs });
|
|
1350
|
-
};
|
|
1351
|
-
|
|
1352
|
-
const ALIGNMENT_FACTORS = { center: 0.5, right: 1 };
|
|
1353
|
-
/**
|
|
1354
|
-
* Remove new line char at the end of line if present
|
|
1355
|
-
*
|
|
1356
|
-
* @param line
|
|
1357
|
-
* @returns Line
|
|
1358
|
-
*/
|
|
1359
|
-
const removeNewLine = (line) => {
|
|
1360
|
-
return last(line.string) === '\n' ? dropLast(line) : line;
|
|
1361
|
-
};
|
|
1362
|
-
const getOverflowLeft = (line) => {
|
|
1363
|
-
return leadingOffset(line) + (line.overflowLeft || 0);
|
|
1364
|
-
};
|
|
1365
|
-
const getOverflowRight = (line) => {
|
|
1366
|
-
return trailingOffset(line) + (line.overflowRight || 0);
|
|
1367
|
-
};
|
|
1368
|
-
/**
|
|
1369
|
-
* Ignore whitespace at the start and end of a line for alignment
|
|
1370
|
-
*
|
|
1371
|
-
* @param line
|
|
1372
|
-
* @returns Line
|
|
1373
|
-
*/
|
|
1374
|
-
const adjustOverflow = (line) => {
|
|
1375
|
-
const overflowLeft = getOverflowLeft(line);
|
|
1376
|
-
const overflowRight = getOverflowRight(line);
|
|
1377
|
-
const x = line.box.x - overflowLeft;
|
|
1378
|
-
const width = line.box.width + overflowLeft + overflowRight;
|
|
1379
|
-
const box = Object.assign({}, line.box, { x, width });
|
|
1380
|
-
return Object.assign({}, line, { box, overflowLeft, overflowRight });
|
|
1381
|
-
};
|
|
1382
|
-
/**
|
|
1383
|
-
* Performs line justification by calling appropiate engine
|
|
1384
|
-
*
|
|
1385
|
-
* @param engines - Engines
|
|
1386
|
-
* @param options - Layout options
|
|
1387
|
-
* @param align - Text align
|
|
1388
|
-
*/
|
|
1389
|
-
const justifyLine$1 = (engines, options, align) => {
|
|
1390
|
-
/**
|
|
1391
|
-
* @param line - Line
|
|
1392
|
-
* @returns Line
|
|
1393
|
-
*/
|
|
1394
|
-
return (line) => {
|
|
1395
|
-
const lineWidth = advanceWidth(line);
|
|
1396
|
-
const alignFactor = ALIGNMENT_FACTORS[align] || 0;
|
|
1397
|
-
const remainingWidth = Math.max(0, line.box.width - lineWidth);
|
|
1398
|
-
const shouldJustify = align === 'justify' || lineWidth > line.box.width;
|
|
1399
|
-
const x = line.box.x + remainingWidth * alignFactor;
|
|
1400
|
-
const box = Object.assign({}, line.box, { x });
|
|
1401
|
-
const newLine = Object.assign({}, line, { box });
|
|
1402
|
-
return shouldJustify ? engines.justification(options)(newLine) : newLine;
|
|
1403
|
-
};
|
|
1404
|
-
};
|
|
1405
|
-
const finalizeLine = (line) => {
|
|
1406
|
-
let lineAscent = 0;
|
|
1407
|
-
let lineDescent = 0;
|
|
1408
|
-
let lineHeight = 0;
|
|
1409
|
-
let lineXAdvance = 0;
|
|
1410
|
-
const runs = line.runs.map((run) => {
|
|
1411
|
-
const height = height$1(run);
|
|
1412
|
-
const ascent = ascent$1(run);
|
|
1413
|
-
const descent$1 = descent(run);
|
|
1414
|
-
const xAdvance = advanceWidth$1(run);
|
|
1415
|
-
lineHeight = Math.max(lineHeight, height);
|
|
1416
|
-
lineAscent = Math.max(lineAscent, ascent);
|
|
1417
|
-
lineDescent = Math.max(lineDescent, descent$1);
|
|
1418
|
-
lineXAdvance += xAdvance;
|
|
1419
|
-
return Object.assign({}, run, { height, ascent, descent: descent$1, xAdvance });
|
|
1420
|
-
});
|
|
1421
|
-
return Object.assign({}, line, {
|
|
1422
|
-
runs,
|
|
1423
|
-
height: lineHeight,
|
|
1424
|
-
ascent: lineAscent,
|
|
1425
|
-
descent: lineDescent,
|
|
1426
|
-
xAdvance: lineXAdvance,
|
|
1427
|
-
});
|
|
1428
|
-
};
|
|
1429
|
-
/**
|
|
1430
|
-
* Finalize line by performing line justification
|
|
1431
|
-
* and text decoration (using appropiate engines)
|
|
1432
|
-
*
|
|
1433
|
-
* @param engines - Engines
|
|
1434
|
-
* @param options - Layout options
|
|
1435
|
-
*/
|
|
1436
|
-
const finalizeBlock = (engines, options) => {
|
|
1437
|
-
/**
|
|
1438
|
-
* @param line - Line
|
|
1439
|
-
* @param i - Line index
|
|
1440
|
-
* @param lines - Total lines
|
|
1441
|
-
* @returns Line
|
|
1442
|
-
*/
|
|
1443
|
-
return (line, index, lines) => {
|
|
1444
|
-
const isLastFragment = index === lines.length - 1;
|
|
1445
|
-
const style = line.runs?.[0]?.attributes || {};
|
|
1446
|
-
const align = isLastFragment ? style.alignLastLine : style.align;
|
|
1447
|
-
return compose(finalizeLine, engines.textDecoration(), justifyLine$1(engines, options, align), adjustOverflow, removeNewLine)(line);
|
|
1448
|
-
};
|
|
1449
|
-
};
|
|
1450
|
-
/**
|
|
1451
|
-
* Finalize line block by performing line justification
|
|
1452
|
-
* and text decoration (using appropiate engines)
|
|
1453
|
-
*
|
|
1454
|
-
* @param engines - Engines
|
|
1455
|
-
* @param options - Layout options
|
|
1456
|
-
*/
|
|
1457
|
-
const finalizeFragments = (engines, options) => {
|
|
1458
|
-
/**
|
|
1459
|
-
* @param paragraphs - Paragraphs
|
|
1460
|
-
* @returns Paragraphs
|
|
1461
|
-
*/
|
|
1462
|
-
return (paragraphs) => {
|
|
1463
|
-
const blockFinalizer = finalizeBlock(engines, options);
|
|
1464
|
-
return paragraphs.map((paragraph) => paragraph.map(blockFinalizer));
|
|
1465
|
-
};
|
|
1466
|
-
};
|
|
1467
|
-
|
|
1468
|
-
const ATTACHMENT_CODE = 0xfffc; // 65532
|
|
1469
|
-
const isReplaceGlyph = (glyph) => glyph.codePoints.includes(ATTACHMENT_CODE);
|
|
1470
|
-
/**
|
|
1471
|
-
* Resolve attachments of run
|
|
1472
|
-
*
|
|
1473
|
-
* @param run
|
|
1474
|
-
* @returns Run
|
|
1475
|
-
*/
|
|
1476
|
-
const resolveRunAttachments = (run) => {
|
|
1477
|
-
if (!run.positions)
|
|
1478
|
-
return run;
|
|
1479
|
-
const glyphs = run.glyphs || [];
|
|
1480
|
-
const attachment = run.attributes?.attachment;
|
|
1481
|
-
if (!attachment)
|
|
1482
|
-
return run;
|
|
1483
|
-
const positions = run.positions.map((position, i) => {
|
|
1484
|
-
const glyph = glyphs[i];
|
|
1485
|
-
if (attachment.width && isReplaceGlyph(glyph)) {
|
|
1486
|
-
return Object.assign({}, position, { xAdvance: attachment.width });
|
|
1487
|
-
}
|
|
1488
|
-
return Object.assign({}, position);
|
|
1489
|
-
});
|
|
1490
|
-
return Object.assign({}, run, { positions });
|
|
1491
|
-
};
|
|
1492
|
-
/**
|
|
1493
|
-
* Resolve attachments for multiple paragraphs
|
|
1494
|
-
*/
|
|
1495
|
-
const resolveAttachments = () => {
|
|
1496
|
-
/**
|
|
1497
|
-
* @param attributedString - Attributed string
|
|
1498
|
-
* @returns Attributed string
|
|
1499
|
-
*/
|
|
1500
|
-
return (attributedString) => {
|
|
1501
|
-
const runs = attributedString.runs.map(resolveRunAttachments);
|
|
1502
|
-
const res = Object.assign({}, attributedString, { runs });
|
|
1503
|
-
return res;
|
|
1504
|
-
};
|
|
1505
|
-
};
|
|
1506
|
-
|
|
1507
|
-
/**
|
|
1508
|
-
* @param attributes - Attributes
|
|
1509
|
-
* @returns Attributes with defaults
|
|
1510
|
-
*/
|
|
1511
|
-
const applyAttributes = (a) => {
|
|
1512
|
-
return {
|
|
1513
|
-
align: a.align || (a.direction === 'rtl' ? 'right' : 'left'),
|
|
1514
|
-
alignLastLine: a.alignLastLine || (a.align === 'justify' ? 'left' : a.align || 'left'),
|
|
1515
|
-
attachment: a.attachment || null,
|
|
1516
|
-
backgroundColor: a.backgroundColor || null,
|
|
1517
|
-
bullet: a.bullet || null,
|
|
1518
|
-
characterSpacing: a.characterSpacing || 0,
|
|
1519
|
-
color: a.color || 'black',
|
|
1520
|
-
direction: a.direction || 'ltr',
|
|
1521
|
-
features: a.features || [],
|
|
1522
|
-
fill: a.fill !== false,
|
|
1523
|
-
font: a.font || [],
|
|
1524
|
-
fontSize: a.fontSize || 12,
|
|
1525
|
-
hangingPunctuation: a.hangingPunctuation || false,
|
|
1526
|
-
hyphenationFactor: a.hyphenationFactor || 0,
|
|
1527
|
-
indent: a.indent || 0,
|
|
1528
|
-
justificationFactor: a.justificationFactor || 1,
|
|
1529
|
-
lineHeight: a.lineHeight || null,
|
|
1530
|
-
lineSpacing: a.lineSpacing || 0,
|
|
1531
|
-
link: a.link || null,
|
|
1532
|
-
marginLeft: a.marginLeft || a.margin || 0,
|
|
1533
|
-
marginRight: a.marginRight || a.margin || 0,
|
|
1534
|
-
opacity: a.opacity,
|
|
1535
|
-
paddingTop: a.paddingTop || a.padding || 0,
|
|
1536
|
-
paragraphSpacing: a.paragraphSpacing || 0,
|
|
1537
|
-
script: a.script || null,
|
|
1538
|
-
shrinkFactor: a.shrinkFactor || 0,
|
|
1539
|
-
strike: a.strike || false,
|
|
1540
|
-
strikeColor: a.strikeColor || a.color || 'black',
|
|
1541
|
-
strikeStyle: a.strikeStyle || 'solid',
|
|
1542
|
-
stroke: a.stroke || false,
|
|
1543
|
-
underline: a.underline || false,
|
|
1544
|
-
underlineColor: a.underlineColor || a.color || 'black',
|
|
1545
|
-
underlineStyle: a.underlineStyle || 'solid',
|
|
1546
|
-
verticalAlign: a.verticalAlign || null,
|
|
1547
|
-
wordSpacing: a.wordSpacing || 0,
|
|
1548
|
-
yOffset: a.yOffset || 0,
|
|
1549
|
-
};
|
|
1550
|
-
};
|
|
1551
|
-
/**
|
|
1552
|
-
* Apply default style to run
|
|
1553
|
-
*
|
|
1554
|
-
* @param run - Run
|
|
1555
|
-
* @returns Run with default styles
|
|
1556
|
-
*/
|
|
1557
|
-
const applyRunStyles = (run) => {
|
|
1558
|
-
const attributes = applyAttributes(run.attributes);
|
|
1559
|
-
return Object.assign({}, run, { attributes });
|
|
1560
|
-
};
|
|
1561
|
-
/**
|
|
1562
|
-
* Apply default attributes for an attributed string
|
|
1563
|
-
*/
|
|
1564
|
-
const applyDefaultStyles = () => {
|
|
1565
|
-
return (attributedString) => {
|
|
1566
|
-
const string = attributedString.string || '';
|
|
1567
|
-
const runs = (attributedString.runs || []).map(applyRunStyles);
|
|
1568
|
-
return { string, runs };
|
|
1569
|
-
};
|
|
1570
|
-
};
|
|
1571
|
-
|
|
1572
|
-
/**
|
|
1573
|
-
* Apply scaling and yOffset for verticalAlign 'sub' and 'super'.
|
|
1574
|
-
*/
|
|
1575
|
-
const verticalAlignment = () => {
|
|
1576
|
-
/**
|
|
1577
|
-
* @param attributedString - Attributed string
|
|
1578
|
-
* @returns Attributed string
|
|
1579
|
-
*/
|
|
1580
|
-
return (attributedString) => {
|
|
1581
|
-
attributedString.runs.forEach((run) => {
|
|
1582
|
-
const { attributes } = run;
|
|
1583
|
-
const { verticalAlign } = attributes;
|
|
1584
|
-
if (verticalAlign === 'sub') {
|
|
1585
|
-
attributes.yOffset = -0.2;
|
|
1586
|
-
}
|
|
1587
|
-
else if (verticalAlign === 'super') {
|
|
1588
|
-
attributes.yOffset = 0.4;
|
|
1589
|
-
}
|
|
1590
|
-
});
|
|
1591
|
-
return attributedString;
|
|
1592
|
-
};
|
|
1593
|
-
};
|
|
1594
|
-
|
|
1595
|
-
const bidi$1 = bidiFactory();
|
|
1596
|
-
/**
|
|
1597
|
-
* @param runs
|
|
1598
|
-
* @returns Bidi levels
|
|
1599
|
-
*/
|
|
1600
|
-
const getBidiLevels = (runs) => {
|
|
1601
|
-
return runs.reduce((acc, run) => {
|
|
1602
|
-
const length = run.end - run.start;
|
|
1603
|
-
const levels = repeat(run.attributes.bidiLevel, length);
|
|
1604
|
-
return acc.concat(levels);
|
|
1605
|
-
}, []);
|
|
1606
|
-
};
|
|
1607
|
-
/**
|
|
1608
|
-
* Perform bidi mirroring
|
|
1609
|
-
*/
|
|
1610
|
-
const mirrorString = () => {
|
|
1611
|
-
/**
|
|
1612
|
-
* @param attributedString - Attributed string
|
|
1613
|
-
* @returns Attributed string
|
|
1614
|
-
*/
|
|
1615
|
-
return (attributedString) => {
|
|
1616
|
-
const levels = getBidiLevels(attributedString.runs);
|
|
1617
|
-
let updatedString = '';
|
|
1618
|
-
attributedString.string.split('').forEach((char, index) => {
|
|
1619
|
-
const isRTL = levels[index] % 2 === 1;
|
|
1620
|
-
const mirroredChar = isRTL
|
|
1621
|
-
? bidi$1.getMirroredCharacter(attributedString.string.charAt(index))
|
|
1622
|
-
: null;
|
|
1623
|
-
updatedString += mirroredChar || char;
|
|
1624
|
-
});
|
|
1625
|
-
const result = {
|
|
1626
|
-
...attributedString,
|
|
1627
|
-
string: updatedString,
|
|
1628
|
-
};
|
|
1629
|
-
return result;
|
|
1630
|
-
};
|
|
1631
|
-
};
|
|
1632
|
-
|
|
1633
|
-
/**
|
|
1634
|
-
* A LayoutEngine is the main object that performs text layout.
|
|
1635
|
-
* It accepts an AttributedString and a Container object
|
|
1636
|
-
* to layout text into, and uses several helper objects to perform
|
|
1637
|
-
* various layout tasks. These objects can be overridden to customize
|
|
1638
|
-
* layout behavior.
|
|
1639
|
-
*/
|
|
1640
|
-
const layoutEngine = (engines) => {
|
|
1641
|
-
return (attributedString, container, options = {}) => {
|
|
1642
|
-
const processParagraph = compose(resolveYOffset(), resolveAttachments(), verticalAlignment(), wrapWords(engines, options), generateGlyphs(), mirrorString(), preprocessRuns(engines));
|
|
1643
|
-
const processParagraphs = (paragraphs) => paragraphs.map(processParagraph);
|
|
1644
|
-
return compose(finalizeFragments(engines, options), bidiReordering(), typesetter(engines, options, container), processParagraphs, splitParagraphs(), applyDefaultStyles())(attributedString);
|
|
1645
|
-
};
|
|
1646
|
-
};
|
|
1647
|
-
|
|
1648
|
-
const bidi = bidiFactory();
|
|
1649
|
-
const bidiEngine = () => {
|
|
1650
|
-
/**
|
|
1651
|
-
* @param attributedString - Attributed string
|
|
1652
|
-
* @returns Attributed string
|
|
1653
|
-
*/
|
|
1654
|
-
return (attributedString) => {
|
|
1655
|
-
const { string } = attributedString;
|
|
1656
|
-
const direction = attributedString.runs[0]?.attributes.direction;
|
|
1657
|
-
const { levels } = bidi.getEmbeddingLevels(string, direction);
|
|
1658
|
-
let lastLevel = null;
|
|
1659
|
-
let lastIndex = 0;
|
|
1660
|
-
let index = 0;
|
|
1661
|
-
const runs = [];
|
|
1662
|
-
for (let i = 0; i < levels.length; i += 1) {
|
|
1663
|
-
const level = levels[i];
|
|
1664
|
-
if (level !== lastLevel) {
|
|
1665
|
-
if (lastLevel !== null) {
|
|
1666
|
-
runs.push({
|
|
1667
|
-
start: lastIndex,
|
|
1668
|
-
end: index,
|
|
1669
|
-
attributes: { bidiLevel: lastLevel },
|
|
1670
|
-
});
|
|
1671
|
-
}
|
|
1672
|
-
lastIndex = index;
|
|
1673
|
-
lastLevel = level;
|
|
1674
|
-
}
|
|
1675
|
-
index += 1;
|
|
1676
|
-
}
|
|
1677
|
-
if (lastIndex < string.length) {
|
|
1678
|
-
runs.push({
|
|
1679
|
-
start: lastIndex,
|
|
1680
|
-
end: string.length,
|
|
1681
|
-
attributes: { bidiLevel: lastLevel },
|
|
1682
|
-
});
|
|
1683
|
-
}
|
|
1684
|
-
const result = { string, runs };
|
|
1685
|
-
return result;
|
|
1686
|
-
};
|
|
1687
|
-
};
|
|
1688
|
-
|
|
1689
|
-
const INFINITY = 10000;
|
|
1690
|
-
const getNextBreakpoint = (subnodes, widths, lineNumber) => {
|
|
1691
|
-
let position = null;
|
|
1692
|
-
let minimumBadness = Infinity;
|
|
1693
|
-
const sum = { width: 0, stretch: 0, shrink: 0 };
|
|
1694
|
-
const lineLength = widths[Math.min(lineNumber, widths.length - 1)];
|
|
1695
|
-
const calculateRatio = (node) => {
|
|
1696
|
-
const stretch = 'stretch' in node ? node.stretch : null;
|
|
1697
|
-
if (sum.width < lineLength) {
|
|
1698
|
-
if (!stretch)
|
|
1699
|
-
return INFINITY;
|
|
1700
|
-
return sum.stretch - stretch > 0
|
|
1701
|
-
? (lineLength - sum.width) / sum.stretch
|
|
1702
|
-
: INFINITY;
|
|
1703
|
-
}
|
|
1704
|
-
const shrink = 'shrink' in node ? node.shrink : null;
|
|
1705
|
-
if (sum.width > lineLength) {
|
|
1706
|
-
if (!shrink)
|
|
1707
|
-
return INFINITY;
|
|
1708
|
-
return sum.shrink - shrink > 0
|
|
1709
|
-
? (lineLength - sum.width) / sum.shrink
|
|
1710
|
-
: INFINITY;
|
|
1711
|
-
}
|
|
1712
|
-
return 0;
|
|
1713
|
-
};
|
|
1714
|
-
for (let i = 0; i < subnodes.length; i += 1) {
|
|
1715
|
-
const node = subnodes[i];
|
|
1716
|
-
if (node.type === 'box') {
|
|
1717
|
-
sum.width += node.width;
|
|
1718
|
-
}
|
|
1719
|
-
if (node.type === 'glue') {
|
|
1720
|
-
sum.width += node.width;
|
|
1721
|
-
sum.stretch += node.stretch;
|
|
1722
|
-
sum.shrink += node.shrink;
|
|
1723
|
-
}
|
|
1724
|
-
if (sum.width - sum.shrink > lineLength) {
|
|
1725
|
-
if (position === null) {
|
|
1726
|
-
let j = i === 0 ? i + 1 : i;
|
|
1727
|
-
while (j < subnodes.length &&
|
|
1728
|
-
(subnodes[j].type === 'glue' || subnodes[j].type === 'penalty')) {
|
|
1729
|
-
j++;
|
|
1730
|
-
}
|
|
1731
|
-
position = j - 1;
|
|
1732
|
-
}
|
|
1733
|
-
break;
|
|
1734
|
-
}
|
|
1735
|
-
if (node.type === 'penalty' || node.type === 'glue') {
|
|
1736
|
-
const ratio = calculateRatio(node);
|
|
1737
|
-
const penalty = node.type === 'penalty' ? node.penalty : 0;
|
|
1738
|
-
const badness = 100 * Math.abs(ratio) ** 3 + penalty;
|
|
1739
|
-
if (minimumBadness >= badness) {
|
|
1740
|
-
position = i;
|
|
1741
|
-
minimumBadness = badness;
|
|
1742
|
-
}
|
|
1743
|
-
}
|
|
1744
|
-
}
|
|
1745
|
-
return sum.width - sum.shrink > lineLength ? position : null;
|
|
1746
|
-
};
|
|
1747
|
-
const applyBestFit = (nodes, widths) => {
|
|
1748
|
-
let count = 0;
|
|
1749
|
-
let lineNumber = 0;
|
|
1750
|
-
let subnodes = nodes;
|
|
1751
|
-
const breakpoints = [0];
|
|
1752
|
-
while (subnodes.length > 0) {
|
|
1753
|
-
const breakpoint = getNextBreakpoint(subnodes, widths, lineNumber);
|
|
1754
|
-
if (breakpoint !== null) {
|
|
1755
|
-
count += breakpoint;
|
|
1756
|
-
breakpoints.push(count);
|
|
1757
|
-
subnodes = subnodes.slice(breakpoint + 1, subnodes.length);
|
|
1758
|
-
count++;
|
|
1759
|
-
lineNumber++;
|
|
1760
|
-
}
|
|
1761
|
-
else {
|
|
1762
|
-
subnodes = [];
|
|
1763
|
-
}
|
|
1764
|
-
}
|
|
1765
|
-
return breakpoints;
|
|
1766
|
-
};
|
|
1767
|
-
|
|
1768
|
-
/* eslint-disable max-classes-per-file */
|
|
1769
|
-
class LinkedListNode {
|
|
1770
|
-
data;
|
|
1771
|
-
prev;
|
|
1772
|
-
next;
|
|
1773
|
-
constructor(data) {
|
|
1774
|
-
this.data = data;
|
|
1775
|
-
this.prev = null;
|
|
1776
|
-
this.next = null;
|
|
1777
|
-
}
|
|
1778
|
-
}
|
|
1779
|
-
class LinkedList {
|
|
1780
|
-
static Node = LinkedListNode;
|
|
1781
|
-
head;
|
|
1782
|
-
tail;
|
|
1783
|
-
listSize;
|
|
1784
|
-
listLength;
|
|
1785
|
-
constructor() {
|
|
1786
|
-
this.head = null;
|
|
1787
|
-
this.tail = null;
|
|
1788
|
-
this.listSize = 0;
|
|
1789
|
-
this.listLength = 0;
|
|
1790
|
-
}
|
|
1791
|
-
isLinked(node) {
|
|
1792
|
-
return !((node &&
|
|
1793
|
-
node.prev === null &&
|
|
1794
|
-
node.next === null &&
|
|
1795
|
-
this.tail !== node &&
|
|
1796
|
-
this.head !== node) ||
|
|
1797
|
-
this.isEmpty());
|
|
1798
|
-
}
|
|
1799
|
-
size() {
|
|
1800
|
-
return this.listSize;
|
|
1801
|
-
}
|
|
1802
|
-
isEmpty() {
|
|
1803
|
-
return this.listSize === 0;
|
|
1804
|
-
}
|
|
1805
|
-
first() {
|
|
1806
|
-
return this.head;
|
|
1807
|
-
}
|
|
1808
|
-
last() {
|
|
1809
|
-
return this.last;
|
|
1810
|
-
}
|
|
1811
|
-
forEach(callback) {
|
|
1812
|
-
let node = this.head;
|
|
1813
|
-
while (node !== null) {
|
|
1814
|
-
callback(node);
|
|
1815
|
-
node = node.next;
|
|
1816
|
-
}
|
|
1817
|
-
}
|
|
1818
|
-
at(i) {
|
|
1819
|
-
let node = this.head;
|
|
1820
|
-
let index = 0;
|
|
1821
|
-
if (i >= this.listLength || i < 0) {
|
|
1822
|
-
return null;
|
|
1823
|
-
}
|
|
1824
|
-
while (node !== null) {
|
|
1825
|
-
if (i === index) {
|
|
1826
|
-
return node;
|
|
1827
|
-
}
|
|
1828
|
-
node = node.next;
|
|
1829
|
-
index += 1;
|
|
1830
|
-
}
|
|
1831
|
-
return null;
|
|
1832
|
-
}
|
|
1833
|
-
insertAfter(node, newNode) {
|
|
1834
|
-
if (!this.isLinked(node))
|
|
1835
|
-
return this;
|
|
1836
|
-
newNode.prev = node;
|
|
1837
|
-
newNode.next = node.next;
|
|
1838
|
-
if (node.next === null) {
|
|
1839
|
-
this.tail = newNode;
|
|
1840
|
-
}
|
|
1841
|
-
else {
|
|
1842
|
-
node.next.prev = newNode;
|
|
1843
|
-
}
|
|
1844
|
-
node.next = newNode;
|
|
1845
|
-
this.listSize += 1;
|
|
1846
|
-
return this;
|
|
1847
|
-
}
|
|
1848
|
-
insertBefore(node, newNode) {
|
|
1849
|
-
if (!this.isLinked(node))
|
|
1850
|
-
return this;
|
|
1851
|
-
newNode.prev = node.prev;
|
|
1852
|
-
newNode.next = node;
|
|
1853
|
-
if (node.prev === null) {
|
|
1854
|
-
this.head = newNode;
|
|
1855
|
-
}
|
|
1856
|
-
else {
|
|
1857
|
-
node.prev.next = newNode;
|
|
1858
|
-
}
|
|
1859
|
-
node.prev = newNode;
|
|
1860
|
-
this.listSize += 1;
|
|
1861
|
-
return this;
|
|
1862
|
-
}
|
|
1863
|
-
push(node) {
|
|
1864
|
-
if (this.head === null) {
|
|
1865
|
-
this.unshift(node);
|
|
1866
|
-
}
|
|
1867
|
-
else {
|
|
1868
|
-
this.insertAfter(this.tail, node);
|
|
1869
|
-
}
|
|
1870
|
-
return this;
|
|
1871
|
-
}
|
|
1872
|
-
unshift(node) {
|
|
1873
|
-
if (this.head === null) {
|
|
1874
|
-
this.head = node;
|
|
1875
|
-
this.tail = node;
|
|
1876
|
-
node.prev = null;
|
|
1877
|
-
node.next = null;
|
|
1878
|
-
this.listSize += 1;
|
|
1879
|
-
}
|
|
1880
|
-
else {
|
|
1881
|
-
this.insertBefore(this.head, node);
|
|
1882
|
-
}
|
|
1883
|
-
return this;
|
|
1884
|
-
}
|
|
1885
|
-
remove(node) {
|
|
1886
|
-
if (!this.isLinked(node))
|
|
1887
|
-
return this;
|
|
1888
|
-
if (node.prev === null) {
|
|
1889
|
-
this.head = node.next;
|
|
1890
|
-
}
|
|
1891
|
-
else {
|
|
1892
|
-
node.prev.next = node.next;
|
|
1893
|
-
}
|
|
1894
|
-
if (node.next === null) {
|
|
1895
|
-
this.tail = node.prev;
|
|
1896
|
-
}
|
|
1897
|
-
else {
|
|
1898
|
-
node.next.prev = node.prev;
|
|
1899
|
-
}
|
|
1900
|
-
this.listSize -= 1;
|
|
1901
|
-
return this;
|
|
1902
|
-
}
|
|
1903
|
-
}
|
|
1904
|
-
|
|
1905
|
-
/**
|
|
1906
|
-
* Licensed under the new BSD License.
|
|
1907
|
-
* Copyright 2009-2010, Bram Stein
|
|
1908
|
-
* All rights reserved.
|
|
1909
|
-
*/
|
|
1910
|
-
function breakpoint(position, demerits, line, fitnessClass, totals, previous) {
|
|
1911
|
-
return {
|
|
1912
|
-
position,
|
|
1913
|
-
demerits,
|
|
1914
|
-
line,
|
|
1915
|
-
fitnessClass,
|
|
1916
|
-
totals: totals || {
|
|
1917
|
-
width: 0,
|
|
1918
|
-
stretch: 0,
|
|
1919
|
-
shrink: 0,
|
|
1920
|
-
},
|
|
1921
|
-
previous,
|
|
1922
|
-
};
|
|
1923
|
-
}
|
|
1924
|
-
function computeCost(nodes, lineLengths, sum, end, active, currentLine) {
|
|
1925
|
-
let width = sum.width - active.totals.width;
|
|
1926
|
-
let stretch = 0;
|
|
1927
|
-
let shrink = 0;
|
|
1928
|
-
// If the current line index is within the list of linelengths, use it, otherwise use
|
|
1929
|
-
// the last line length of the list.
|
|
1930
|
-
const lineLength = currentLine < lineLengths.length
|
|
1931
|
-
? lineLengths[currentLine - 1]
|
|
1932
|
-
: lineLengths[lineLengths.length - 1];
|
|
1933
|
-
if (nodes[end].type === 'penalty') {
|
|
1934
|
-
width += nodes[end].width;
|
|
1935
|
-
}
|
|
1936
|
-
// Calculate the stretch ratio
|
|
1937
|
-
if (width < lineLength) {
|
|
1938
|
-
stretch = sum.stretch - active.totals.stretch;
|
|
1939
|
-
if (stretch > 0) {
|
|
1940
|
-
return (lineLength - width) / stretch;
|
|
1941
|
-
}
|
|
1942
|
-
return linebreak.infinity;
|
|
1943
|
-
}
|
|
1944
|
-
// Calculate the shrink ratio
|
|
1945
|
-
if (width > lineLength) {
|
|
1946
|
-
shrink = sum.shrink - active.totals.shrink;
|
|
1947
|
-
if (shrink > 0) {
|
|
1948
|
-
return (lineLength - width) / shrink;
|
|
1949
|
-
}
|
|
1950
|
-
return linebreak.infinity;
|
|
1951
|
-
}
|
|
1952
|
-
// perfect match
|
|
1953
|
-
return 0;
|
|
1954
|
-
}
|
|
1955
|
-
// Add width, stretch and shrink values from the current
|
|
1956
|
-
// break point up to the next box or forced penalty.
|
|
1957
|
-
function computeSum(nodes, sum, breakPointIndex) {
|
|
1958
|
-
const result = {
|
|
1959
|
-
width: sum.width,
|
|
1960
|
-
stretch: sum.stretch,
|
|
1961
|
-
shrink: sum.shrink,
|
|
1962
|
-
};
|
|
1963
|
-
for (let i = breakPointIndex; i < nodes.length; i += 1) {
|
|
1964
|
-
const node = nodes[i];
|
|
1965
|
-
if (node.type === 'glue') {
|
|
1966
|
-
result.width += node.width;
|
|
1967
|
-
result.stretch += node.stretch;
|
|
1968
|
-
result.shrink += node.shrink;
|
|
1969
|
-
}
|
|
1970
|
-
else if (node.type === 'box' ||
|
|
1971
|
-
(node.type === 'penalty' &&
|
|
1972
|
-
node.penalty === -linebreak.infinity &&
|
|
1973
|
-
i > breakPointIndex)) {
|
|
1974
|
-
break;
|
|
1975
|
-
}
|
|
1976
|
-
}
|
|
1977
|
-
return result;
|
|
1978
|
-
}
|
|
1979
|
-
function findBestBreakpoints(activeNodes) {
|
|
1980
|
-
const breakpoints = [];
|
|
1981
|
-
if (activeNodes.size() === 0)
|
|
1982
|
-
return [];
|
|
1983
|
-
let tmp = { data: { demerits: Infinity } };
|
|
1984
|
-
// Find the best active node (the one with the least total demerits.)
|
|
1985
|
-
activeNodes.forEach((node) => {
|
|
1986
|
-
if (node.data.demerits < tmp.data.demerits) {
|
|
1987
|
-
tmp = node;
|
|
1988
|
-
}
|
|
1989
|
-
});
|
|
1990
|
-
while (tmp !== null) {
|
|
1991
|
-
breakpoints.push(tmp.data.position);
|
|
1992
|
-
tmp = tmp.data.previous;
|
|
1993
|
-
}
|
|
1994
|
-
return breakpoints.reverse();
|
|
1995
|
-
}
|
|
1996
|
-
/**
|
|
1997
|
-
* @param nodes
|
|
1998
|
-
* @param availableWidths
|
|
1999
|
-
* @param tolerance
|
|
2000
|
-
* @preserve Knuth and Plass line breaking algorithm in JavaScript
|
|
2001
|
-
*/
|
|
2002
|
-
const linebreak = (nodes, availableWidths, tolerance) => {
|
|
2003
|
-
// Demerits are used as a way to penalize bad line breaks
|
|
2004
|
-
// - line: applied to each line, depending on how much spaces need to stretch or shrink
|
|
2005
|
-
// - flagged: applied when consecutive lines end in hyphenation
|
|
2006
|
-
// - fitness: algorithm groups lines into fitness classes based on how loose or tight the spacing is.
|
|
2007
|
-
// if a paragraph has consecutive lines from different fitness classes,
|
|
2008
|
-
// a fitness demerit is applied to maintain visual consistency.
|
|
2009
|
-
const options = {
|
|
2010
|
-
demerits: { line: 10, flagged: 100, fitness: 3000 },
|
|
2011
|
-
tolerance: tolerance || 3,
|
|
2012
|
-
};
|
|
2013
|
-
const activeNodes = new LinkedList();
|
|
2014
|
-
const sum = { width: 0, stretch: 0, shrink: 0 };
|
|
2015
|
-
const lineLengths = availableWidths;
|
|
2016
|
-
// Add an active node for the start of the paragraph.
|
|
2017
|
-
activeNodes.push(new LinkedList.Node(breakpoint(0, 0, 0, 0, undefined, null)));
|
|
2018
|
-
// The main loop of the algorithm
|
|
2019
|
-
function mainLoop(node, index, nodes) {
|
|
2020
|
-
let active = activeNodes.first();
|
|
2021
|
-
// The inner loop iterates through all the active nodes with line < currentLine and then
|
|
2022
|
-
// breaks out to insert the new active node candidates before looking at the next active
|
|
2023
|
-
// nodes for the next lines. The result of this is that the active node list is always
|
|
2024
|
-
// sorted by line number.
|
|
2025
|
-
while (active !== null) {
|
|
2026
|
-
let currentLine = 0;
|
|
2027
|
-
// Candidates fo each fitness class
|
|
2028
|
-
const candidates = [
|
|
2029
|
-
{ active: undefined, demerits: Infinity },
|
|
2030
|
-
{ active: undefined, demerits: Infinity },
|
|
2031
|
-
{ active: undefined, demerits: Infinity },
|
|
2032
|
-
{ active: undefined, demerits: Infinity },
|
|
2033
|
-
];
|
|
2034
|
-
// Iterate through the linked list of active nodes to find new potential active nodes and deactivate current active nodes.
|
|
2035
|
-
while (active !== null) {
|
|
2036
|
-
currentLine = active.data.line + 1;
|
|
2037
|
-
const ratio = computeCost(nodes, lineLengths, sum, index, active.data, currentLine);
|
|
2038
|
-
// Deactive nodes when the distance between the current active node and the
|
|
2039
|
-
// current node becomes too large (i.e. it exceeds the stretch limit and the stretch
|
|
2040
|
-
// ratio becomes negative) or when the current node is a forced break (i.e. the end
|
|
2041
|
-
// of the paragraph when we want to remove all active nodes, but possibly have a final
|
|
2042
|
-
// candidate active node---if the paragraph can be set using the given tolerance value.)
|
|
2043
|
-
if (ratio < -1 ||
|
|
2044
|
-
(node.type === 'penalty' && node.penalty === -linebreak.infinity)) {
|
|
2045
|
-
activeNodes.remove(active);
|
|
2046
|
-
}
|
|
2047
|
-
// If the ratio is within the valid range of -1 <= ratio <= tolerance calculate the
|
|
2048
|
-
// total demerits and record a candidate active node.
|
|
2049
|
-
if (ratio >= -1 && ratio <= options.tolerance) {
|
|
2050
|
-
const badness = 100 * Math.pow(Math.abs(ratio), 3);
|
|
2051
|
-
let demerits = 0;
|
|
2052
|
-
// Positive penalty
|
|
2053
|
-
if (node.type === 'penalty' && node.penalty >= 0) {
|
|
2054
|
-
demerits =
|
|
2055
|
-
Math.pow(options.demerits.line + badness, 2) +
|
|
2056
|
-
Math.pow(node.penalty, 2);
|
|
2057
|
-
// Negative penalty but not a forced break
|
|
2058
|
-
}
|
|
2059
|
-
else if (node.type === 'penalty' &&
|
|
2060
|
-
node.penalty !== -linebreak.infinity) {
|
|
2061
|
-
demerits =
|
|
2062
|
-
Math.pow(options.demerits.line + badness, 2) -
|
|
2063
|
-
Math.pow(node.penalty, 2);
|
|
2064
|
-
// All other cases
|
|
2065
|
-
}
|
|
2066
|
-
else {
|
|
2067
|
-
demerits = Math.pow(options.demerits.line + badness, 2);
|
|
2068
|
-
}
|
|
2069
|
-
if (node.type === 'penalty' &&
|
|
2070
|
-
nodes[active.data.position].type === 'penalty') {
|
|
2071
|
-
demerits +=
|
|
2072
|
-
options.demerits.flagged *
|
|
2073
|
-
node.flagged *
|
|
2074
|
-
// @ts-expect-error node is penalty here
|
|
2075
|
-
nodes[active.data.position].flagged;
|
|
2076
|
-
}
|
|
2077
|
-
// Calculate the fitness class for this candidate active node.
|
|
2078
|
-
let currentClass;
|
|
2079
|
-
if (ratio < -0.5) {
|
|
2080
|
-
currentClass = 0;
|
|
2081
|
-
}
|
|
2082
|
-
else if (ratio <= 0.5) {
|
|
2083
|
-
currentClass = 1;
|
|
2084
|
-
}
|
|
2085
|
-
else if (ratio <= 1) {
|
|
2086
|
-
currentClass = 2;
|
|
2087
|
-
}
|
|
2088
|
-
else {
|
|
2089
|
-
currentClass = 3;
|
|
2090
|
-
}
|
|
2091
|
-
// Add a fitness penalty to the demerits if the fitness classes of two adjacent lines differ too much.
|
|
2092
|
-
if (Math.abs(currentClass - active.data.fitnessClass) > 1) {
|
|
2093
|
-
demerits += options.demerits.fitness;
|
|
2094
|
-
}
|
|
2095
|
-
// Add the total demerits of the active node to get the total demerits of this candidate node.
|
|
2096
|
-
demerits += active.data.demerits;
|
|
2097
|
-
// Only store the best candidate for each fitness class
|
|
2098
|
-
if (demerits < candidates[currentClass].demerits) {
|
|
2099
|
-
candidates[currentClass] = { active, demerits };
|
|
2100
|
-
}
|
|
2101
|
-
}
|
|
2102
|
-
active = active.next;
|
|
2103
|
-
// Stop iterating through active nodes to insert new candidate active nodes in the active list
|
|
2104
|
-
// before moving on to the active nodes for the next line.
|
|
2105
|
-
// TODO: The Knuth and Plass paper suggests a conditional for currentLine < j0. This means paragraphs
|
|
2106
|
-
// with identical line lengths will not be sorted by line number. Find out if that is a desirable outcome.
|
|
2107
|
-
// For now I left this out, as it only adds minimal overhead to the algorithm and keeping the active node
|
|
2108
|
-
// list sorted has a higher priority.
|
|
2109
|
-
if (active !== null && active.data.line >= currentLine) {
|
|
2110
|
-
break;
|
|
2111
|
-
}
|
|
2112
|
-
}
|
|
2113
|
-
const tmpSum = computeSum(nodes, sum, index);
|
|
2114
|
-
for (let fitnessClass = 0; fitnessClass < candidates.length; fitnessClass += 1) {
|
|
2115
|
-
const candidate = candidates[fitnessClass];
|
|
2116
|
-
if (candidate.demerits === Infinity)
|
|
2117
|
-
continue;
|
|
2118
|
-
const newNode = new LinkedList.Node(breakpoint(index, candidate.demerits, candidate.active.data.line + 1, fitnessClass, tmpSum, candidate.active));
|
|
2119
|
-
if (active !== null) {
|
|
2120
|
-
activeNodes.insertBefore(active, newNode);
|
|
2121
|
-
}
|
|
2122
|
-
else {
|
|
2123
|
-
activeNodes.push(newNode);
|
|
2124
|
-
}
|
|
2125
|
-
}
|
|
2126
|
-
}
|
|
2127
|
-
}
|
|
2128
|
-
nodes.forEach((node, index, nodes) => {
|
|
2129
|
-
if (node.type === 'box') {
|
|
2130
|
-
sum.width += node.width;
|
|
2131
|
-
return;
|
|
2132
|
-
}
|
|
2133
|
-
if (node.type === 'glue') {
|
|
2134
|
-
const precedesBox = index > 0 && nodes[index - 1].type === 'box';
|
|
2135
|
-
if (precedesBox)
|
|
2136
|
-
mainLoop(node, index, nodes);
|
|
2137
|
-
sum.width += node.width;
|
|
2138
|
-
sum.stretch += node.stretch;
|
|
2139
|
-
sum.shrink += node.shrink;
|
|
2140
|
-
return;
|
|
2141
|
-
}
|
|
2142
|
-
if (node.type === 'penalty' && node.penalty !== linebreak.infinity) {
|
|
2143
|
-
mainLoop(node, index, nodes);
|
|
2144
|
-
}
|
|
2145
|
-
});
|
|
2146
|
-
return findBestBreakpoints(activeNodes);
|
|
2147
|
-
};
|
|
2148
|
-
linebreak.infinity = 10000;
|
|
2149
|
-
linebreak.glue = (width, start, end, stretch, shrink) => ({
|
|
2150
|
-
type: 'glue',
|
|
2151
|
-
start,
|
|
2152
|
-
end,
|
|
2153
|
-
width,
|
|
2154
|
-
stretch,
|
|
2155
|
-
shrink,
|
|
2156
|
-
});
|
|
2157
|
-
linebreak.box = (width, start, end, hyphenated = false) => ({
|
|
2158
|
-
type: 'box',
|
|
2159
|
-
width,
|
|
2160
|
-
start,
|
|
2161
|
-
end,
|
|
2162
|
-
hyphenated,
|
|
2163
|
-
});
|
|
2164
|
-
linebreak.penalty = (width, penalty, flagged) => ({
|
|
2165
|
-
type: 'penalty',
|
|
2166
|
-
width,
|
|
2167
|
-
penalty,
|
|
2168
|
-
flagged,
|
|
2169
|
-
});
|
|
2170
|
-
|
|
2171
|
-
/**
|
|
2172
|
-
* Add scalar to run
|
|
2173
|
-
*
|
|
2174
|
-
* @param index - Scalar
|
|
2175
|
-
* @param run - Run
|
|
2176
|
-
* @returns Added run
|
|
2177
|
-
*/
|
|
2178
|
-
const add = (index, run) => {
|
|
2179
|
-
const start = run.start + index;
|
|
2180
|
-
const end = run.end + index;
|
|
2181
|
-
return Object.assign({}, run, { start, end });
|
|
2182
|
-
};
|
|
2183
|
-
|
|
2184
|
-
/**
|
|
2185
|
-
* Get run length
|
|
2186
|
-
*
|
|
2187
|
-
* @param run - Run
|
|
2188
|
-
* @returns Length
|
|
2189
|
-
*/
|
|
2190
|
-
const length = (run) => {
|
|
2191
|
-
return run.end - run.start;
|
|
2192
|
-
};
|
|
2193
|
-
|
|
2194
|
-
/**
|
|
2195
|
-
* Concats two runs into one
|
|
2196
|
-
*
|
|
2197
|
-
* @param runA - First run
|
|
2198
|
-
* @param runB - Second run
|
|
2199
|
-
* @returns Concatenated run
|
|
2200
|
-
*/
|
|
2201
|
-
const concat = (runA, runB) => {
|
|
2202
|
-
const end = runA.end + length(runB);
|
|
2203
|
-
const glyphs = (runA.glyphs || []).concat(runB.glyphs || []);
|
|
2204
|
-
const positions = (runA.positions || []).concat(runB.positions || []);
|
|
2205
|
-
const attributes = Object.assign({}, runA.attributes, runB.attributes);
|
|
2206
|
-
const runAIndices = runA.glyphIndices || [];
|
|
2207
|
-
const runALastIndex = last(runAIndices) || 0;
|
|
2208
|
-
const runBIndices = (runB.glyphIndices || []).map((i) => i + runALastIndex + 1);
|
|
2209
|
-
const glyphIndices = normalize(runAIndices.concat(runBIndices));
|
|
2210
|
-
return Object.assign({}, runA, {
|
|
2211
|
-
end,
|
|
2212
|
-
glyphs,
|
|
2213
|
-
positions,
|
|
2214
|
-
attributes,
|
|
2215
|
-
glyphIndices,
|
|
2216
|
-
});
|
|
2217
|
-
};
|
|
2218
|
-
|
|
2219
|
-
/**
|
|
2220
|
-
* Insert glyph to run in the given index
|
|
2221
|
-
*
|
|
2222
|
-
* @param index - Index
|
|
2223
|
-
* @param glyph - Glyph
|
|
2224
|
-
* @param run - Run
|
|
2225
|
-
* @returns Run with glyph
|
|
2226
|
-
*/
|
|
2227
|
-
const insertGlyph$1 = (index, glyph, run) => {
|
|
2228
|
-
if (!glyph)
|
|
2229
|
-
return run;
|
|
2230
|
-
// Split resolves ligature splitting in case new glyph breaks some
|
|
2231
|
-
const leadingRun = slice$1(0, index, run);
|
|
2232
|
-
const trailingRun = slice$1(index, Infinity, run);
|
|
2233
|
-
return concat(append$1(glyph, leadingRun), trailingRun);
|
|
2234
|
-
};
|
|
2235
|
-
/**
|
|
2236
|
-
* Insert either glyph or code point to run in the given index
|
|
2237
|
-
*
|
|
2238
|
-
* @param index - Index
|
|
2239
|
-
* @param value - Glyph or codePoint
|
|
2240
|
-
* @param run - Run
|
|
2241
|
-
* @returns Run with glyph
|
|
2242
|
-
*/
|
|
2243
|
-
const insert = (index, value, run) => {
|
|
2244
|
-
const font = getFont(run);
|
|
2245
|
-
const glyph = isNumber(value) ? fromCodePoint(value, font) : value;
|
|
2246
|
-
return insertGlyph$1(index, glyph, run);
|
|
2247
|
-
};
|
|
2248
|
-
|
|
2249
|
-
/**
|
|
2250
|
-
* Get run index at char index
|
|
2251
|
-
*
|
|
2252
|
-
* @param index - Char index
|
|
2253
|
-
* @param attributedString - Attributed string
|
|
2254
|
-
* @returns Run index
|
|
2255
|
-
*/
|
|
2256
|
-
const runIndexAt = (index, attributedString) => {
|
|
2257
|
-
return runIndexAt$1(index, attributedString.runs);
|
|
2258
|
-
};
|
|
2259
|
-
|
|
2260
|
-
/**
|
|
2261
|
-
* Insert glyph into attributed string
|
|
2262
|
-
*
|
|
2263
|
-
* @param index - Index
|
|
2264
|
-
* @param glyph - Glyph or code point
|
|
2265
|
-
* @param attributedString - Attributed string
|
|
2266
|
-
* @returns Attributed string with new glyph
|
|
2267
|
-
*/
|
|
2268
|
-
const insertGlyph = (index, glyph, attributedString) => {
|
|
2269
|
-
const runIndex = runIndexAt(index, attributedString);
|
|
2270
|
-
// Add glyph to the end if run index invalid
|
|
2271
|
-
if (runIndex === -1)
|
|
2272
|
-
return append(glyph, attributedString);
|
|
2273
|
-
const codePoints = [glyph] ;
|
|
2274
|
-
const string = attributedString.string.slice(0, index) +
|
|
2275
|
-
stringFromCodePoints(codePoints) +
|
|
2276
|
-
attributedString.string.slice(index);
|
|
2277
|
-
const runs = attributedString.runs.map((run, i) => {
|
|
2278
|
-
if (i === runIndex)
|
|
2279
|
-
return insert(index - run.start, glyph, run);
|
|
2280
|
-
if (i > runIndex)
|
|
2281
|
-
return add(codePoints.length, run);
|
|
2282
|
-
return run;
|
|
2283
|
-
});
|
|
2284
|
-
return Object.assign({}, attributedString, { string, runs });
|
|
2285
|
-
};
|
|
2286
|
-
|
|
2287
|
-
/**
|
|
2288
|
-
* Advance width between two string indices
|
|
2289
|
-
*
|
|
2290
|
-
* @param start - Glyph index
|
|
2291
|
-
* @param end - Glyph index
|
|
2292
|
-
* @param run - Run
|
|
2293
|
-
* @returns Advanced width run
|
|
2294
|
-
*/
|
|
2295
|
-
const advanceWidthBetween$1 = (start, end, run) => {
|
|
2296
|
-
const runStart = run.start || 0;
|
|
2297
|
-
const glyphStartIndex = Math.max(0, glyphIndexAt(start - runStart, run));
|
|
2298
|
-
const glyphEndIndex = Math.max(0, glyphIndexAt(end - runStart, run));
|
|
2299
|
-
const positions = (run.positions || []).slice(glyphStartIndex, glyphEndIndex);
|
|
2300
|
-
return advanceWidth$2(positions);
|
|
2301
|
-
};
|
|
2302
|
-
|
|
2303
|
-
/**
|
|
2304
|
-
* Advance width between start and end
|
|
2305
|
-
* Does not consider ligature splitting for the moment.
|
|
2306
|
-
* Check performance impact on supporting this
|
|
2307
|
-
*
|
|
2308
|
-
* @param start - Start offset
|
|
2309
|
-
* @param end - End offset
|
|
2310
|
-
* @param attributedString
|
|
2311
|
-
* @returns Advance width
|
|
2312
|
-
*/
|
|
2313
|
-
const advanceWidthBetween = (start, end, attributedString) => {
|
|
2314
|
-
const runs = filter(start, end, attributedString.runs);
|
|
2315
|
-
return runs.reduce((acc, run) => acc + advanceWidthBetween$1(start, end, run), 0);
|
|
2316
|
-
};
|
|
2317
|
-
|
|
2318
|
-
const HYPHEN = 0x002d;
|
|
2319
|
-
const TOLERANCE_STEPS = 5;
|
|
2320
|
-
const TOLERANCE_LIMIT = 50;
|
|
2321
|
-
const opts = {
|
|
2322
|
-
width: 3,
|
|
2323
|
-
stretch: 6,
|
|
2324
|
-
shrink: 9,
|
|
2325
|
-
};
|
|
2326
|
-
/**
|
|
2327
|
-
* Slice attributed string to many lines
|
|
2328
|
-
*
|
|
2329
|
-
* @param attributedString - Attributed string
|
|
2330
|
-
* @param nodes
|
|
2331
|
-
* @param breaks
|
|
2332
|
-
* @returns Attributed strings
|
|
2333
|
-
*/
|
|
2334
|
-
const breakLines = (attributedString, nodes, breaks) => {
|
|
2335
|
-
let start = 0;
|
|
2336
|
-
let end = null;
|
|
2337
|
-
const lines = breaks.reduce((acc, breakPoint) => {
|
|
2338
|
-
const node = nodes[breakPoint];
|
|
2339
|
-
const prevNode = nodes[breakPoint - 1];
|
|
2340
|
-
// Last breakpoint corresponds to K&P mandatory final glue
|
|
2341
|
-
if (breakPoint === nodes.length - 1)
|
|
2342
|
-
return acc;
|
|
2343
|
-
let line;
|
|
2344
|
-
if (node.type === 'penalty') {
|
|
2345
|
-
// @ts-expect-error penalty node will always preceed box or glue node
|
|
2346
|
-
end = prevNode.end;
|
|
2347
|
-
line = slice(start, end, attributedString);
|
|
2348
|
-
line = insertGlyph(line.string.length, HYPHEN, line);
|
|
2349
|
-
}
|
|
2350
|
-
else {
|
|
2351
|
-
end = node.end;
|
|
2352
|
-
line = slice(start, end, attributedString);
|
|
2353
|
-
}
|
|
2354
|
-
start = end;
|
|
2355
|
-
return [...acc, line];
|
|
2356
|
-
}, []);
|
|
2357
|
-
// Last line
|
|
2358
|
-
lines.push(slice(start, attributedString.string.length, attributedString));
|
|
2359
|
-
return lines;
|
|
2360
|
-
};
|
|
2361
|
-
/**
|
|
2362
|
-
* Return Knuth & Plass nodes based on line and previously calculated syllables
|
|
2363
|
-
*
|
|
2364
|
-
* @param attributedString - Attributed string
|
|
2365
|
-
* @param attributes - Attributes
|
|
2366
|
-
* @param options - Layout options
|
|
2367
|
-
* @returns ?
|
|
2368
|
-
*/
|
|
2369
|
-
const getNodes = (attributedString, { align }, options) => {
|
|
2370
|
-
let start = 0;
|
|
2371
|
-
const hyphenWidth = 5;
|
|
2372
|
-
const { syllables } = attributedString;
|
|
2373
|
-
const hyphenPenalty = options.hyphenationPenalty || (align === 'justify' ? 100 : 600);
|
|
2374
|
-
const result = syllables.reduce((acc, s, index) => {
|
|
2375
|
-
const width = advanceWidthBetween(start, start + s.length, attributedString);
|
|
2376
|
-
if (s.trim() === '') {
|
|
2377
|
-
const stretch = (width * opts.width) / opts.stretch;
|
|
2378
|
-
const shrink = (width * opts.width) / opts.shrink;
|
|
2379
|
-
const end = start + s.length;
|
|
2380
|
-
// Add glue node. Glue nodes are used to fill the space between words.
|
|
2381
|
-
acc.push(linebreak.glue(width, start, end, stretch, shrink));
|
|
2382
|
-
}
|
|
2383
|
-
else {
|
|
2384
|
-
const hyphenated = syllables[index + 1] !== ' ';
|
|
2385
|
-
const end = start + s.length;
|
|
2386
|
-
// Add box node. Box nodes are used to represent words.
|
|
2387
|
-
acc.push(linebreak.box(width, start, end, hyphenated));
|
|
2388
|
-
if (syllables[index + 1] && hyphenated) {
|
|
2389
|
-
// Add penalty node. Penalty nodes are used to represent hyphenation points.
|
|
2390
|
-
acc.push(linebreak.penalty(hyphenWidth, hyphenPenalty, 1));
|
|
2391
|
-
}
|
|
2392
|
-
}
|
|
2393
|
-
start += s.length;
|
|
2394
|
-
return acc;
|
|
2395
|
-
}, []);
|
|
2396
|
-
// Add mandatory final glue
|
|
2397
|
-
result.push(linebreak.glue(0, start, start, linebreak.infinity, 0));
|
|
2398
|
-
result.push(linebreak.penalty(0, -linebreak.infinity, 1));
|
|
2399
|
-
return result;
|
|
2400
|
-
};
|
|
2401
|
-
/**
|
|
2402
|
-
* @param attributedString - Attributed string
|
|
2403
|
-
* @returns Attributes
|
|
2404
|
-
*/
|
|
2405
|
-
const getAttributes = (attributedString) => {
|
|
2406
|
-
return attributedString.runs?.[0]?.attributes || {};
|
|
2407
|
-
};
|
|
2408
|
-
/**
|
|
2409
|
-
* Performs Knuth & Plass line breaking algorithm
|
|
2410
|
-
* Fallbacks to best fit algorithm if latter not successful
|
|
2411
|
-
*
|
|
2412
|
-
* @param options - Layout options
|
|
2413
|
-
*/
|
|
2414
|
-
const linebreaker = (options) => {
|
|
2415
|
-
/**
|
|
2416
|
-
* @param attributedString - Attributed string
|
|
2417
|
-
* @param availableWidths - Available widths
|
|
2418
|
-
* @returns Attributed string
|
|
2419
|
-
*/
|
|
2420
|
-
return (attributedString, availableWidths) => {
|
|
2421
|
-
let tolerance = options.tolerance || 4;
|
|
2422
|
-
const attributes = getAttributes(attributedString);
|
|
2423
|
-
const nodes = getNodes(attributedString, attributes, options);
|
|
2424
|
-
let breaks = linebreak(nodes, availableWidths, tolerance);
|
|
2425
|
-
// Try again with a higher tolerance if the line breaking failed.
|
|
2426
|
-
while (breaks.length === 0 && tolerance < TOLERANCE_LIMIT) {
|
|
2427
|
-
tolerance += TOLERANCE_STEPS;
|
|
2428
|
-
breaks = linebreak(nodes, availableWidths, tolerance);
|
|
2429
|
-
}
|
|
2430
|
-
if (breaks.length === 0 || (breaks.length === 1 && breaks[0] === 0)) {
|
|
2431
|
-
breaks = applyBestFit(nodes, availableWidths);
|
|
2432
|
-
}
|
|
2433
|
-
return breakLines(attributedString, nodes, breaks.slice(1));
|
|
2434
|
-
};
|
|
2435
|
-
};
|
|
2436
|
-
|
|
2437
|
-
var Direction;
|
|
2438
|
-
(function (Direction) {
|
|
2439
|
-
Direction[Direction["GROW"] = 0] = "GROW";
|
|
2440
|
-
Direction[Direction["SHRINK"] = 1] = "SHRINK";
|
|
2441
|
-
})(Direction || (Direction = {}));
|
|
2442
|
-
const WHITESPACE_PRIORITY = 1;
|
|
2443
|
-
const LETTER_PRIORITY = 2;
|
|
2444
|
-
const EXPAND_WHITESPACE_FACTOR = {
|
|
2445
|
-
before: 0.5,
|
|
2446
|
-
after: 0.5,
|
|
2447
|
-
priority: WHITESPACE_PRIORITY,
|
|
2448
|
-
unconstrained: false,
|
|
2449
|
-
};
|
|
2450
|
-
const EXPAND_CHAR_FACTOR = {
|
|
2451
|
-
before: 0.14453125, // 37/256
|
|
2452
|
-
after: 0.14453125,
|
|
2453
|
-
priority: LETTER_PRIORITY,
|
|
2454
|
-
unconstrained: false,
|
|
2455
|
-
};
|
|
2456
|
-
const SHRINK_WHITESPACE_FACTOR = {
|
|
2457
|
-
before: -0.04296875, // -11/256
|
|
2458
|
-
after: -0.04296875,
|
|
2459
|
-
priority: WHITESPACE_PRIORITY,
|
|
2460
|
-
unconstrained: false,
|
|
2461
|
-
};
|
|
2462
|
-
const SHRINK_CHAR_FACTOR = {
|
|
2463
|
-
before: -0.04296875,
|
|
2464
|
-
after: -0.04296875,
|
|
2465
|
-
priority: LETTER_PRIORITY,
|
|
2466
|
-
unconstrained: false,
|
|
2467
|
-
};
|
|
2468
|
-
const getCharFactor = (direction, options) => {
|
|
2469
|
-
const expandCharFactor = options.expandCharFactor || {};
|
|
2470
|
-
const shrinkCharFactor = options.shrinkCharFactor || {};
|
|
2471
|
-
return direction === Direction.GROW
|
|
2472
|
-
? Object.assign({}, EXPAND_CHAR_FACTOR, expandCharFactor)
|
|
2473
|
-
: Object.assign({}, SHRINK_CHAR_FACTOR, shrinkCharFactor);
|
|
2474
|
-
};
|
|
2475
|
-
const getWhitespaceFactor = (direction, options) => {
|
|
2476
|
-
const expandWhitespaceFactor = options.expandWhitespaceFactor || {};
|
|
2477
|
-
const shrinkWhitespaceFactor = options.shrinkWhitespaceFactor || {};
|
|
2478
|
-
return direction === Direction.GROW
|
|
2479
|
-
? Object.assign({}, EXPAND_WHITESPACE_FACTOR, expandWhitespaceFactor)
|
|
2480
|
-
: Object.assign({}, SHRINK_WHITESPACE_FACTOR, shrinkWhitespaceFactor);
|
|
2481
|
-
};
|
|
2482
|
-
const factor = (direction, options) => (glyphs) => {
|
|
2483
|
-
const charFactor = getCharFactor(direction, options);
|
|
2484
|
-
const whitespaceFactor = getWhitespaceFactor(direction, options);
|
|
2485
|
-
const factors = [];
|
|
2486
|
-
for (let index = 0; index < glyphs.length; index += 1) {
|
|
2487
|
-
let f;
|
|
2488
|
-
const glyph = glyphs[index];
|
|
2489
|
-
if (isWhiteSpace(glyph)) {
|
|
2490
|
-
f = Object.assign({}, whitespaceFactor);
|
|
2491
|
-
if (index === glyphs.length - 1) {
|
|
2492
|
-
f.before = 0;
|
|
2493
|
-
if (index > 0) {
|
|
2494
|
-
factors[index - 1].after = 0;
|
|
2495
|
-
}
|
|
2496
|
-
}
|
|
2497
|
-
}
|
|
2498
|
-
else if (glyph.isMark && index > 0) {
|
|
2499
|
-
f = Object.assign({}, factors[index - 1]);
|
|
2500
|
-
f.before = 0;
|
|
2501
|
-
factors[index - 1].after = 0;
|
|
2502
|
-
}
|
|
2503
|
-
else {
|
|
2504
|
-
f = Object.assign({}, charFactor);
|
|
2505
|
-
}
|
|
2506
|
-
factors.push(f);
|
|
2507
|
-
}
|
|
2508
|
-
return factors;
|
|
2509
|
-
};
|
|
2510
|
-
const getFactors = (gap, line, options) => {
|
|
2511
|
-
const direction = gap > 0 ? Direction.GROW : Direction.SHRINK;
|
|
2512
|
-
const getFactor = factor(direction, options);
|
|
2513
|
-
const factors = line.runs.reduce((acc, run) => {
|
|
2514
|
-
return acc.concat(getFactor(run.glyphs));
|
|
2515
|
-
}, []);
|
|
2516
|
-
factors[0].before = 0;
|
|
2517
|
-
factors[factors.length - 1].after = 0;
|
|
2518
|
-
return factors;
|
|
2519
|
-
};
|
|
2520
|
-
|
|
2521
|
-
const KASHIDA_PRIORITY = 0;
|
|
2522
|
-
const NULL_PRIORITY = 3;
|
|
2523
|
-
const getDistances = (gap, factors) => {
|
|
2524
|
-
let total = 0;
|
|
2525
|
-
const priorities = [];
|
|
2526
|
-
const unconstrained = [];
|
|
2527
|
-
for (let priority = KASHIDA_PRIORITY; priority <= NULL_PRIORITY; priority += 1) {
|
|
2528
|
-
priorities[priority] = unconstrained[priority] = 0;
|
|
2529
|
-
}
|
|
2530
|
-
// sum the factors at each priority
|
|
2531
|
-
for (let j = 0; j < factors.length; j += 1) {
|
|
2532
|
-
const f = factors[j];
|
|
2533
|
-
const sum = f.before + f.after;
|
|
2534
|
-
total += sum;
|
|
2535
|
-
priorities[f.priority] += sum;
|
|
2536
|
-
if (f.unconstrained) {
|
|
2537
|
-
unconstrained[f.priority] += sum;
|
|
2538
|
-
}
|
|
2539
|
-
}
|
|
2540
|
-
// choose the priorities that need to be applied
|
|
2541
|
-
let highestPriority = -1;
|
|
2542
|
-
let highestPrioritySum = 0;
|
|
2543
|
-
let remainingGap = gap;
|
|
2544
|
-
let priority;
|
|
2545
|
-
for (priority = KASHIDA_PRIORITY; priority <= NULL_PRIORITY; priority += 1) {
|
|
2546
|
-
const prioritySum = priorities[priority];
|
|
2547
|
-
if (prioritySum !== 0) {
|
|
2548
|
-
if (highestPriority === -1) {
|
|
2549
|
-
highestPriority = priority;
|
|
2550
|
-
highestPrioritySum = prioritySum;
|
|
2551
|
-
}
|
|
2552
|
-
// if this priority covers the remaining gap, we're done
|
|
2553
|
-
if (Math.abs(remainingGap) <= Math.abs(prioritySum)) {
|
|
2554
|
-
priorities[priority] = remainingGap / prioritySum;
|
|
2555
|
-
unconstrained[priority] = 0;
|
|
2556
|
-
remainingGap = 0;
|
|
2557
|
-
break;
|
|
2558
|
-
}
|
|
2559
|
-
// mark that we need to use 100% of the adjustment from
|
|
2560
|
-
// this priority, and subtract the space that it consumes
|
|
2561
|
-
priorities[priority] = 1;
|
|
2562
|
-
remainingGap -= prioritySum;
|
|
2563
|
-
// if this priority has unconstrained glyphs, let them consume the remaining space
|
|
2564
|
-
if (unconstrained[priority] !== 0) {
|
|
2565
|
-
unconstrained[priority] = remainingGap / unconstrained[priority];
|
|
2566
|
-
remainingGap = 0;
|
|
2567
|
-
break;
|
|
2568
|
-
}
|
|
2569
|
-
}
|
|
2570
|
-
}
|
|
2571
|
-
// zero out remaining priorities (if any)
|
|
2572
|
-
for (let p = priority + 1; p <= NULL_PRIORITY; p += 1) {
|
|
2573
|
-
priorities[p] = 0;
|
|
2574
|
-
unconstrained[p] = 0;
|
|
2575
|
-
}
|
|
2576
|
-
// if there is still space left over, assign it to the highest priority that we saw.
|
|
2577
|
-
// this violates their factors, but it only happens in extreme cases
|
|
2578
|
-
if (remainingGap > 0 && highestPriority > -1) {
|
|
2579
|
-
priorities[highestPriority] =
|
|
2580
|
-
(highestPrioritySum + (gap - total)) / highestPrioritySum;
|
|
2581
|
-
}
|
|
2582
|
-
// create and return an array of distances to add to each glyph's advance
|
|
2583
|
-
const distances = [];
|
|
2584
|
-
for (let index = 0; index < factors.length; index += 1) {
|
|
2585
|
-
// the distance to add to this glyph is the sum of the space to add
|
|
2586
|
-
// after this glyph, and the space to add before the next glyph
|
|
2587
|
-
const f = factors[index];
|
|
2588
|
-
const next = factors[index + 1];
|
|
2589
|
-
let dist = f.after * priorities[f.priority];
|
|
2590
|
-
if (next) {
|
|
2591
|
-
dist += next.before * priorities[next.priority];
|
|
2592
|
-
}
|
|
2593
|
-
// if this glyph is unconstrained, add the unconstrained distance as well
|
|
2594
|
-
if (f.unconstrained) {
|
|
2595
|
-
dist += f.after * unconstrained[f.priority];
|
|
2596
|
-
if (next) {
|
|
2597
|
-
dist += next.before * unconstrained[next.priority];
|
|
2598
|
-
}
|
|
2599
|
-
}
|
|
2600
|
-
distances.push(dist);
|
|
2601
|
-
}
|
|
2602
|
-
return distances;
|
|
2603
|
-
};
|
|
2604
|
-
|
|
2605
|
-
/**
|
|
2606
|
-
* Adjust run positions by given distances
|
|
2607
|
-
*
|
|
2608
|
-
* @param distances
|
|
2609
|
-
* @param line
|
|
2610
|
-
* @returns Line
|
|
2611
|
-
*/
|
|
2612
|
-
const justifyLine = (distances, line) => {
|
|
2613
|
-
let index = 0;
|
|
2614
|
-
for (const run of line.runs) {
|
|
2615
|
-
for (const position of run.positions) {
|
|
2616
|
-
position.xAdvance += distances[index++];
|
|
2617
|
-
}
|
|
2618
|
-
}
|
|
2619
|
-
return line;
|
|
2620
|
-
};
|
|
2621
|
-
/**
|
|
2622
|
-
* A JustificationEngine is used by a Typesetter to perform line fragment
|
|
2623
|
-
* justification. This implementation is based on a description of Apple's
|
|
2624
|
-
* justification algorithm from a PDF in the Apple Font Tools package.
|
|
2625
|
-
*
|
|
2626
|
-
* @param options - Layout options
|
|
2627
|
-
*/
|
|
2628
|
-
const justification = (options) => {
|
|
2629
|
-
/**
|
|
2630
|
-
* @param line
|
|
2631
|
-
* @returns Line
|
|
2632
|
-
*/
|
|
2633
|
-
return (line) => {
|
|
2634
|
-
const gap = line.box.width - advanceWidth(line);
|
|
2635
|
-
if (gap === 0)
|
|
2636
|
-
return line; // Exact fit
|
|
2637
|
-
const factors = getFactors(gap, line, options);
|
|
2638
|
-
const distances = getDistances(gap, factors);
|
|
2639
|
-
return justifyLine(distances, line);
|
|
2640
|
-
};
|
|
2641
|
-
};
|
|
2642
|
-
|
|
2643
|
-
/**
|
|
2644
|
-
* Returns attributed string ascent
|
|
2645
|
-
*
|
|
2646
|
-
* @param attributedString - Attributed string
|
|
2647
|
-
* @returns Ascent
|
|
2648
|
-
*/
|
|
2649
|
-
const ascent = (attributedString) => {
|
|
2650
|
-
const reducer = (acc, run) => Math.max(acc, ascent$1(run));
|
|
2651
|
-
return attributedString.runs.reduce(reducer, 0);
|
|
2652
|
-
};
|
|
2653
|
-
|
|
2654
|
-
// The base font size used for calculating underline thickness.
|
|
2655
|
-
const BASE_FONT_SIZE = 12;
|
|
2656
|
-
/**
|
|
2657
|
-
* A TextDecorationEngine is used by a Typesetter to generate
|
|
2658
|
-
* DecorationLines for a line fragment, including underlines
|
|
2659
|
-
* and strikes.
|
|
2660
|
-
*/
|
|
2661
|
-
const textDecoration = () => (line) => {
|
|
2662
|
-
let x = line.overflowLeft || 0;
|
|
2663
|
-
const overflowRight = line.overflowRight || 0;
|
|
2664
|
-
const maxX = advanceWidth(line) - overflowRight;
|
|
2665
|
-
line.decorationLines = [];
|
|
2666
|
-
for (let i = 0; i < line.runs.length; i += 1) {
|
|
2667
|
-
const run = line.runs[i];
|
|
2668
|
-
const width = Math.min(maxX - x, advanceWidth$1(run));
|
|
2669
|
-
const thickness = Math.max(0.5, Math.floor(run.attributes.fontSize / BASE_FONT_SIZE));
|
|
2670
|
-
if (run.attributes.underline) {
|
|
2671
|
-
const rect = {
|
|
2672
|
-
x,
|
|
2673
|
-
y: ascent(line) + thickness * 2,
|
|
2674
|
-
width,
|
|
2675
|
-
height: thickness,
|
|
2676
|
-
};
|
|
2677
|
-
const decorationLine = {
|
|
2678
|
-
rect,
|
|
2679
|
-
opacity: run.attributes.opacity,
|
|
2680
|
-
color: run.attributes.underlineColor || 'black',
|
|
2681
|
-
style: run.attributes.underlineStyle || 'solid',
|
|
2682
|
-
};
|
|
2683
|
-
line.decorationLines.push(decorationLine);
|
|
2684
|
-
}
|
|
2685
|
-
if (run.attributes.strike) {
|
|
2686
|
-
const y = ascent(line) - ascent$1(run) / 3;
|
|
2687
|
-
const rect = { x, y, width, height: thickness };
|
|
2688
|
-
const decorationLine = {
|
|
2689
|
-
rect,
|
|
2690
|
-
opacity: run.attributes.opacity,
|
|
2691
|
-
color: run.attributes.strikeColor || 'black',
|
|
2692
|
-
style: run.attributes.strikeStyle || 'solid',
|
|
2693
|
-
};
|
|
2694
|
-
line.decorationLines.push(decorationLine);
|
|
2695
|
-
}
|
|
2696
|
-
x += width;
|
|
2697
|
-
}
|
|
2698
|
-
return line;
|
|
2699
|
-
};
|
|
2700
|
-
|
|
2701
|
-
const ignoredScripts = ['Common', 'Inherited', 'Unknown'];
|
|
2702
|
-
/**
|
|
2703
|
-
* Resolves unicode script in runs, grouping equal runs together
|
|
2704
|
-
*/
|
|
2705
|
-
const scriptItemizer = () => {
|
|
2706
|
-
/**
|
|
2707
|
-
* @param attributedString - Attributed string
|
|
2708
|
-
* @returns Attributed string
|
|
2709
|
-
*/
|
|
2710
|
-
return (attributedString) => {
|
|
2711
|
-
const { string } = attributedString;
|
|
2712
|
-
let lastScript = 'Unknown';
|
|
2713
|
-
let lastIndex = 0;
|
|
2714
|
-
let index = 0;
|
|
2715
|
-
const runs = [];
|
|
2716
|
-
if (!string)
|
|
2717
|
-
return empty();
|
|
2718
|
-
for (let i = 0; i < string.length; i += 1) {
|
|
2719
|
-
const char = string[i];
|
|
2720
|
-
const codePoint = char.codePointAt(0);
|
|
2721
|
-
const script = $747425b437e121da$export$2e2bcd8739ae039.getScript(codePoint);
|
|
2722
|
-
if (script !== lastScript && !ignoredScripts.includes(script)) {
|
|
2723
|
-
if (lastScript !== 'Unknown') {
|
|
2724
|
-
runs.push({
|
|
2725
|
-
start: lastIndex,
|
|
2726
|
-
end: index,
|
|
2727
|
-
attributes: { script: lastScript },
|
|
2728
|
-
});
|
|
2729
|
-
}
|
|
2730
|
-
lastIndex = index;
|
|
2731
|
-
lastScript = script;
|
|
2732
|
-
}
|
|
2733
|
-
index += char.length;
|
|
2734
|
-
}
|
|
2735
|
-
if (lastIndex < string.length) {
|
|
2736
|
-
runs.push({
|
|
2737
|
-
start: lastIndex,
|
|
2738
|
-
end: string.length,
|
|
2739
|
-
attributes: { script: lastScript },
|
|
2740
|
-
});
|
|
2741
|
-
}
|
|
2742
|
-
const result = { string, runs: runs };
|
|
2743
|
-
return result;
|
|
2744
|
-
};
|
|
2745
|
-
};
|
|
2746
|
-
|
|
2747
|
-
const SOFT_HYPHEN = '\u00ad';
|
|
2748
|
-
const hyphenator = hyphen(pattern);
|
|
2749
|
-
/**
|
|
2750
|
-
* @param word
|
|
2751
|
-
* @returns Word parts
|
|
2752
|
-
*/
|
|
2753
|
-
const splitHyphen = (word) => {
|
|
2754
|
-
return word.split(SOFT_HYPHEN);
|
|
2755
|
-
};
|
|
2756
|
-
const cache = {};
|
|
2757
|
-
/**
|
|
2758
|
-
* @param word
|
|
2759
|
-
* @returns Word parts
|
|
2760
|
-
*/
|
|
2761
|
-
const getParts = (word) => {
|
|
2762
|
-
const base = word.includes(SOFT_HYPHEN) ? word : hyphenator(word);
|
|
2763
|
-
return splitHyphen(base);
|
|
2764
|
-
};
|
|
2765
|
-
const wordHyphenation = () => {
|
|
2766
|
-
/**
|
|
2767
|
-
* @param word - Word
|
|
2768
|
-
* @returns Word parts
|
|
2769
|
-
*/
|
|
2770
|
-
return (word) => {
|
|
2771
|
-
const cacheKey = `_${word}`;
|
|
2772
|
-
if (isNil(word))
|
|
2773
|
-
return [];
|
|
2774
|
-
if (cache[cacheKey])
|
|
2775
|
-
return cache[cacheKey];
|
|
2776
|
-
cache[cacheKey] = getParts(word);
|
|
2777
|
-
return cache[cacheKey];
|
|
2778
|
-
};
|
|
2779
|
-
};
|
|
2780
|
-
|
|
2781
|
-
const IGNORED_CODE_POINTS = [173];
|
|
2782
|
-
const getFontSize = (run) => run.attributes.fontSize || 12;
|
|
2783
|
-
const pickFontFromFontStack = (codePoint, fontStack, lastFont) => {
|
|
2784
|
-
const fontStackWithFallback = [...fontStack, lastFont];
|
|
2785
|
-
for (let i = 0; i < fontStackWithFallback.length; i += 1) {
|
|
2786
|
-
const font = fontStackWithFallback[i];
|
|
2787
|
-
if (!IGNORED_CODE_POINTS.includes(codePoint) &&
|
|
2788
|
-
font &&
|
|
2789
|
-
font.hasGlyphForCodePoint &&
|
|
2790
|
-
font.hasGlyphForCodePoint(codePoint)) {
|
|
2791
|
-
return font;
|
|
2792
|
-
}
|
|
2793
|
-
}
|
|
2794
|
-
return fontStack.at(-1);
|
|
2795
|
-
};
|
|
2796
|
-
const fontSubstitution = () => ({ string, runs }) => {
|
|
2797
|
-
let lastFont = null;
|
|
2798
|
-
let lastFontSize = null;
|
|
2799
|
-
let lastIndex = 0;
|
|
2800
|
-
let index = 0;
|
|
2801
|
-
const res = [];
|
|
2802
|
-
for (let i = 0; i < runs.length; i += 1) {
|
|
2803
|
-
const run = runs[i];
|
|
2804
|
-
if (string.length === 0) {
|
|
2805
|
-
res.push({
|
|
2806
|
-
start: 0,
|
|
2807
|
-
end: 0,
|
|
2808
|
-
attributes: { font: run.attributes.font },
|
|
2809
|
-
});
|
|
2810
|
-
break;
|
|
2811
|
-
}
|
|
2812
|
-
const chars = string.slice(run.start, run.end);
|
|
2813
|
-
for (let j = 0; j < chars.length; j += 1) {
|
|
2814
|
-
const char = chars[j];
|
|
2815
|
-
const codePoint = char.codePointAt(0);
|
|
2816
|
-
// If the default font does not have a glyph and the fallback font does, we use it
|
|
2817
|
-
const font = pickFontFromFontStack(codePoint, run.attributes.font, lastFont);
|
|
2818
|
-
const fontSize = getFontSize(run);
|
|
2819
|
-
// If anything that would impact res has changed, update it
|
|
2820
|
-
if (font !== lastFont ||
|
|
2821
|
-
fontSize !== lastFontSize ||
|
|
2822
|
-
font.unitsPerEm !== lastFont.unitsPerEm) {
|
|
2823
|
-
if (lastFont) {
|
|
2824
|
-
res.push({
|
|
2825
|
-
start: lastIndex,
|
|
2826
|
-
end: index,
|
|
2827
|
-
attributes: {
|
|
2828
|
-
font: [lastFont],
|
|
2829
|
-
scale: lastFontSize / lastFont.unitsPerEm,
|
|
2830
|
-
},
|
|
2831
|
-
});
|
|
2832
|
-
}
|
|
2833
|
-
lastFont = font;
|
|
2834
|
-
lastFontSize = fontSize;
|
|
2835
|
-
lastIndex = index;
|
|
2836
|
-
}
|
|
2837
|
-
index += char.length;
|
|
2838
|
-
}
|
|
2839
|
-
}
|
|
2840
|
-
if (lastIndex < string.length) {
|
|
2841
|
-
const fontSize = getFontSize(last(runs));
|
|
2842
|
-
res.push({
|
|
2843
|
-
start: lastIndex,
|
|
2844
|
-
end: string.length,
|
|
2845
|
-
attributes: {
|
|
2846
|
-
font: [lastFont],
|
|
2847
|
-
scale: fontSize / lastFont.unitsPerEm,
|
|
2848
|
-
},
|
|
2849
|
-
});
|
|
2850
|
-
}
|
|
2851
|
-
return { string, runs: res };
|
|
2852
|
-
};
|
|
2853
|
-
|
|
2854
|
-
export { bidiEngine as bidi, layoutEngine as default, fontSubstitution, fromFragments, justification, linebreaker, scriptItemizer, textDecoration, wordHyphenation };
|