From 98c14b4f4588d55ecf1beb92a16e704e4489dd4f Mon Sep 17 00:00:00 2001 From: Wojciech Lewicki Date: Tue, 16 Aug 2022 12:00:32 +0200 Subject: [PATCH] chore: add CI for JS, iOS and Android formatting (#1782) Added CI workflow and local pre-commit hook for formatting and linting the newly added JS, iOS and Android code. --- .clang-format | 91 + .github/workflows/js-build-test.yml | 6 +- .husky/pre-commit | 4 + Example/ios/Podfile.lock | 2 +- USAGE.md | 26 +- android/build.gradle | 5 + android/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 60756 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 + android/gradlew | 240 ++ android/gradlew.bat | 91 + android/spotless.gradle | 9 + .../horcrux/svg/FabricEnabledViewGroup.java | 77 +- .../src/main/java/com/horcrux/svg/Brush.java | 392 ++- .../main/java/com/horcrux/svg/CircleView.java | 63 +- .../java/com/horcrux/svg/ClipPathView.java | 50 +- .../java/com/horcrux/svg/DefinitionView.java | 36 +- .../main/java/com/horcrux/svg/DefsView.java | 26 +- .../java/com/horcrux/svg/EllipseView.java | 79 +- .../main/java/com/horcrux/svg/FontData.java | 390 +-- .../com/horcrux/svg/ForeignObjectView.java | 201 +- .../java/com/horcrux/svg/GlyphContext.java | 699 +++-- .../java/com/horcrux/svg/GlyphPathBag.java | 71 +- .../main/java/com/horcrux/svg/GroupView.java | 459 ++- .../main/java/com/horcrux/svg/ImageView.java | 357 +-- .../main/java/com/horcrux/svg/LineView.java | 78 +- .../com/horcrux/svg/LinearGradientView.java | 168 +- .../main/java/com/horcrux/svg/MarkerView.java | 254 +- .../main/java/com/horcrux/svg/MaskView.java | 169 +- .../main/java/com/horcrux/svg/PathParser.java | 1177 ++++---- .../main/java/com/horcrux/svg/PathView.java | 35 +- .../java/com/horcrux/svg/PatternView.java | 286 +- .../main/java/com/horcrux/svg/PropHelper.java | 376 ++- .../com/horcrux/svg/RNSVGMarkerPosition.java | 282 +- .../horcrux/svg/RNSVGRenderableManager.java | 464 ++- .../com/horcrux/svg/RadialGradientView.java | 192 +- .../main/java/com/horcrux/svg/RectView.java | 164 +- .../java/com/horcrux/svg/RenderableView.java | 1159 ++++---- .../horcrux/svg/RenderableViewManager.java | 1701 ++++++----- .../main/java/com/horcrux/svg/SVGLength.java | 42 +- .../main/java/com/horcrux/svg/SvgPackage.java | 93 +- .../main/java/com/horcrux/svg/SvgView.java | 714 +++-- .../java/com/horcrux/svg/SvgViewManager.java | 202 +- .../java/com/horcrux/svg/SvgViewModule.java | 112 +- .../main/java/com/horcrux/svg/SymbolView.java | 109 +- .../main/java/com/horcrux/svg/TSpanView.java | 2308 ++++++++------- .../com/horcrux/svg/TextLayoutAlgorithm.java | 2515 ++++++++--------- .../java/com/horcrux/svg/TextPathView.java | 191 +- .../java/com/horcrux/svg/TextProperties.java | 362 +-- .../main/java/com/horcrux/svg/TextView.java | 406 ++- .../main/java/com/horcrux/svg/UseView.java | 217 +- .../main/java/com/horcrux/svg/ViewBox.java | 146 +- .../java/com/horcrux/svg/VirtualView.java | 996 ++++--- .../horcrux/svg/FabricEnabledViewGroup.java | 2 - apple/Brushes/RNSVGBrush.h | 2 +- apple/Brushes/RNSVGBrush.mm | 11 +- apple/Brushes/RNSVGBrushType.h | 8 +- apple/Brushes/RNSVGContextBrush.mm | 79 +- apple/Brushes/RNSVGPainter.h | 4 +- apple/Brushes/RNSVGPainter.mm | 306 +- apple/Brushes/RNSVGPainterBrush.mm | 39 +- apple/Brushes/RNSVGSolidColorBrush.mm | 56 +- apple/Elements/RNSVGClipPath.mm | 29 +- apple/Elements/RNSVGDefs.mm | 22 +- apple/Elements/RNSVGForeignObject.mm | 236 +- apple/Elements/RNSVGGroup.h | 6 +- apple/Elements/RNSVGGroup.mm | 364 ++- apple/Elements/RNSVGImage.h | 10 +- apple/Elements/RNSVGImage.mm | 344 +-- apple/Elements/RNSVGLinearGradient.h | 2 +- apple/Elements/RNSVGLinearGradient.mm | 156 +- apple/Elements/RNSVGMarker.h | 5 +- apple/Elements/RNSVGMarker.mm | 270 +- apple/Elements/RNSVGMask.mm | 157 +- apple/Elements/RNSVGPath.mm | 47 +- apple/Elements/RNSVGPattern.mm | 254 +- apple/Elements/RNSVGRadialGradient.h | 2 +- apple/Elements/RNSVGRadialGradient.mm | 184 +- apple/Elements/RNSVGSvgView.h | 6 +- apple/Elements/RNSVGSvgView.mm | 492 ++-- apple/Elements/RNSVGSymbol.mm | 136 +- apple/Elements/RNSVGUse.h | 2 +- apple/Elements/RNSVGUse.mm | 241 +- apple/RNSVGNode.h | 14 +- apple/RNSVGNode.mm | 798 +++--- apple/RNSVGRenderable.h | 2 +- apple/RNSVGRenderable.mm | 980 +++---- apple/RNSVGUIKit.h | 13 +- apple/RNSVGUIKit.macos.mm | 71 +- apple/Shapes/RNSVGCircle.h | 7 +- apple/Shapes/RNSVGCircle.mm | 66 +- apple/Shapes/RNSVGEllipse.h | 8 +- apple/Shapes/RNSVGEllipse.mm | 80 +- apple/Shapes/RNSVGLine.h | 8 +- apple/Shapes/RNSVGLine.mm | 84 +- apple/Shapes/RNSVGRect.h | 12 +- apple/Shapes/RNSVGRect.mm | 176 +- apple/Text/RNSVGFontData.h | 40 +- apple/Text/RNSVGFontData.mm | 292 +- apple/Text/RNSVGGlyphContext.h | 24 +- apple/Text/RNSVGGlyphContext.mm | 778 ++--- apple/Text/RNSVGPropHelper.h | 11 +- apple/Text/RNSVGPropHelper.mm | 231 +- apple/Text/RNSVGTSpan.h | 2 +- apple/Text/RNSVGTSpan.mm | 1856 ++++++------ apple/Text/RNSVGText.h | 2 +- apple/Text/RNSVGText.mm | 437 ++- apple/Text/RNSVGTextPath.h | 4 +- apple/Text/RNSVGTextPath.mm | 103 +- apple/Text/RNSVGTextProperties.h | 256 +- apple/Text/RNSVGTextProperties.mm | 264 +- apple/Text/RNSVGTopAlignedLabel.ios.mm | 20 +- apple/Text/RNSVGTopAlignedLabel.macos.mm | 20 +- apple/Utils/RCTConvert+RNSVG.h | 12 +- apple/Utils/RCTConvert+RNSVG.mm | 270 +- apple/Utils/RNSVGBezierElement.h | 5 +- apple/Utils/RNSVGBezierElement.mm | 88 +- apple/Utils/RNSVGCGFCRule.h | 4 +- apple/Utils/RNSVGFabricConversions.h | 296 +- apple/Utils/RNSVGLength.h | 28 +- apple/Utils/RNSVGLength.mm | 119 +- apple/Utils/RNSVGMarkerPosition.h | 10 +- apple/Utils/RNSVGMarkerPosition.mm | 244 +- apple/Utils/RNSVGPathMeasure.mm | 281 +- apple/Utils/RNSVGPathParser.h | 2 +- apple/Utils/RNSVGPathParser.mm | 979 ++++--- apple/Utils/RNSVGUnits.h | 4 +- apple/Utils/RNSVGVBMOS.h | 6 +- apple/Utils/RNSVGVectorEffect.h | 8 +- apple/Utils/RNSVGViewBox.h | 5 +- apple/Utils/RNSVGViewBox.mm | 144 +- apple/ViewManagers/RNSVGCircleManager.mm | 10 +- apple/ViewManagers/RNSVGDefsManager.mm | 2 +- apple/ViewManagers/RNSVGEllipseManager.mm | 10 +- .../ViewManagers/RNSVGForeignObjectManager.mm | 14 +- apple/ViewManagers/RNSVGGroupManager.mm | 32 +- apple/ViewManagers/RNSVGImageManager.mm | 22 +- apple/ViewManagers/RNSVGLineManager.mm | 10 +- .../RNSVGLinearGradientManager.mm | 8 +- apple/ViewManagers/RNSVGMarkerManager.mm | 15 +- apple/ViewManagers/RNSVGMaskManager.mm | 14 +- apple/ViewManagers/RNSVGNodeManager.h | 2 +- apple/ViewManagers/RNSVGNodeManager.mm | 239 +- apple/ViewManagers/RNSVGPathManager.mm | 2 +- apple/ViewManagers/RNSVGPatternManager.mm | 14 +- .../RNSVGRadialGradientManager.mm | 12 +- apple/ViewManagers/RNSVGRectManager.mm | 18 +- apple/ViewManagers/RNSVGRenderableManager.mm | 290 +- apple/ViewManagers/RNSVGSvgViewManager.mm | 84 +- apple/ViewManagers/RNSVGSymbolManager.mm | 2 +- apple/ViewManagers/RNSVGTSpanManager.mm | 2 +- apple/ViewManagers/RNSVGTextManager.mm | 64 +- apple/ViewManagers/RNSVGTextPathManager.mm | 2 +- apple/ViewManagers/RNSVGUseManager.mm | 12 +- package.json | 18 +- scripts/format-java.js | 37 + src/fabric/CircleNativeComponent.js | 30 +- src/fabric/ClipPathNativeComponent.js | 19 +- src/fabric/DefsNativeComponent.js | 7 +- src/fabric/EllipseNativeComponent.js | 32 +- src/fabric/ForeignObjectNativeComponent.js | 49 +- src/fabric/GroupNativeComponent.js | 49 +- src/fabric/ImageNativeComponent.js | 42 +- src/fabric/LineNativeComponent.js | 32 +- src/fabric/LinearGradientNativeComponent.js | 33 +- src/fabric/MarkerNativeComponent.js | 49 +- src/fabric/MaskNativeComponent.js | 52 +- src/fabric/PathNativeComponent.js | 26 +- src/fabric/PatternNativeComponent.js | 51 +- src/fabric/RadialGradientNativeComponent.js | 37 +- src/fabric/RectNativeComponent.js | 40 +- src/fabric/SvgViewNativeComponent.js | 5 +- src/fabric/SymbolNativeComponent.js | 49 +- src/fabric/TSpanNativeComponent.js | 45 +- src/fabric/TextNativeComponent.js | 45 +- src/fabric/TextPathNativeComponent.js | 36 +- src/fabric/UseNativeComponent.js | 38 +- yarn.lock | 239 +- 177 files changed, 16855 insertions(+), 16018 deletions(-) create mode 100644 .clang-format create mode 100755 .husky/pre-commit create mode 100644 android/gradle/wrapper/gradle-wrapper.jar create mode 100644 android/gradle/wrapper/gradle-wrapper.properties create mode 100755 android/gradlew create mode 100644 android/gradlew.bat create mode 100644 android/spotless.gradle create mode 100644 scripts/format-java.js diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..bc20b078 --- /dev/null +++ b/.clang-format @@ -0,0 +1,91 @@ +--- +AccessModifierOffset: -1 +AlignAfterOpenBracket: AlwaysBreak +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlinesLeft: true +AlignOperands: false +AlignTrailingComments: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: true +BinPackArguments: false +BinPackParameters: false +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: false +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +ForEachMacros: [ FOR_EACH_RANGE, FOR_EACH, ] +IncludeCategories: + - Regex: '^<.*\.h(pp)?>' + Priority: 1 + - Regex: '^<.*' + Priority: 2 + - Regex: '.*' + Priority: 3 +IndentCaseLabels: true +IndentWidth: 2 +IndentWrappedFunctionNames: false +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: true +ObjCSpaceBeforeProtocolList: true +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerAlignment: Right +ReflowComments: true +SortIncludes: true +SpaceAfterCStyleCast: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 +TabWidth: 8 +UseTab: Never +--- +Language: ObjC +ColumnLimit: 120 +BreakBeforeBraces: WebKit +... diff --git a/.github/workflows/js-build-test.yml b/.github/workflows/js-build-test.yml index a1d93de9..beae142b 100644 --- a/.github/workflows/js-build-test.yml +++ b/.github/workflows/js-build-test.yml @@ -25,10 +25,8 @@ jobs: run: yarn - name: Build run: yarn bob - - name: Lint - run: yarn lint - - name: Tests - run: yarn jest + - name: Test and lint + run: yarn test - name: Build Example App working-directory: Example/ run: yarn && yarn tsc diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 00000000..5a182ef1 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/usr/bin/env sh +. "$(dirname -- "$0")/_/husky.sh" + +yarn lint-staged diff --git a/Example/ios/Podfile.lock b/Example/ios/Podfile.lock index 36b9a25e..39ba77b0 100644 --- a/Example/ios/Podfile.lock +++ b/Example/ios/Podfile.lock @@ -501,7 +501,7 @@ SPEC CHECKSUMS: CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 DoubleConversion: 831926d9b8bf8166fd87886c4abab286c2422662 FBLazyVector: bcdeff523be9f87a135b7c6fde8736db94904716 - FBReactNativeSpec: 226f8b0f1a2e736a49301883ee34bca88cdc24f6 + FBReactNativeSpec: 0c3f104f594b34d7b3a923cd12e03b0d4e12eaf5 Flipper: 26fc4b7382499f1281eb8cb921e5c3ad6de91fe0 Flipper-Boost-iOSX: fd1e2b8cbef7e662a122412d7ac5f5bea715403c Flipper-DoubleConversion: 57ffbe81ef95306cc9e69c4aa3aeeeeb58a6a28c diff --git a/USAGE.md b/USAGE.md index d61bea00..1de7f0e6 100644 --- a/USAGE.md +++ b/USAGE.md @@ -105,14 +105,14 @@ export default function TestComponent() { }; return ( <> - - {loading && } + + {loading && } ); } @@ -129,16 +129,20 @@ import * as React from 'react'; import { SvgUri } from 'react-native-svg'; export default () => { - const [uri, setUri] = React.useState('https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/not_existing.svg') + const [uri, setUri] = React.useState( + 'https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/not_existing.svg', + ); return ( setUri('https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/ruby.svg')} + onError={() => + setUri('https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/ruby.svg') + } width="100%" height="100%" uri={uri} /> ); -} +}; ``` # Use with svg files diff --git a/android/build.gradle b/android/build.gradle index d3a07594..4b7f3e01 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -10,6 +10,7 @@ buildscript { dependencies { classpath("com.android.tools.build:gradle:3.6.1") + classpath "com.diffplug.spotless:spotless-plugin-gradle:5.15.0" } } } @@ -26,6 +27,10 @@ if (isNewArchitectureEnabled()) { apply plugin: "com.facebook.react" } +if (project == rootProject) { + apply from: 'spotless.gradle' +} + apply plugin: 'com.android.library' def safeExtGet(prop, fallback) { diff --git a/android/gradle/wrapper/gradle-wrapper.jar b/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..249e5832f090a2944b7473328c07c9755baa3196 GIT binary patch literal 60756 zcmb5WV{~QRw(p$^Dz@00IL3?^hro$gg*4VI_WAaTyVM5Foj~O|-84 z$;06hMwt*rV;^8iB z1~&0XWpYJmG?Ts^K9PC62H*`G}xom%S%yq|xvG~FIfP=9*f zZoDRJBm*Y0aId=qJ?7dyb)6)JGWGwe)MHeNSzhi)Ko6J<-m@v=a%NsP537lHe0R* z`If4$aaBA#S=w!2z&m>{lpTy^Lm^mg*3?M&7HFv}7K6x*cukLIGX;bQG|QWdn{%_6 zHnwBKr84#B7Z+AnBXa16a?or^R?+>$4`}{*a_>IhbjvyTtWkHw)|ay)ahWUd-qq$~ zMbh6roVsj;_qnC-R{G+Cy6bApVOinSU-;(DxUEl!i2)1EeQ9`hrfqj(nKI7?Z>Xur zoJz-a`PxkYit1HEbv|jy%~DO^13J-ut986EEG=66S}D3!L}Efp;Bez~7tNq{QsUMm zh9~(HYg1pA*=37C0}n4g&bFbQ+?-h-W}onYeE{q;cIy%eZK9wZjSwGvT+&Cgv z?~{9p(;bY_1+k|wkt_|N!@J~aoY@|U_RGoWX<;p{Nu*D*&_phw`8jYkMNpRTWx1H* z>J-Mi_!`M468#5Aix$$u1M@rJEIOc?k^QBc?T(#=n&*5eS#u*Y)?L8Ha$9wRWdH^3D4|Ps)Y?m0q~SiKiSfEkJ!=^`lJ(%W3o|CZ zSrZL-Xxc{OrmsQD&s~zPfNJOpSZUl%V8tdG%ei}lQkM+z@-4etFPR>GOH9+Y_F<3=~SXln9Kb-o~f>2a6Xz@AS3cn^;c_>lUwlK(n>z?A>NbC z`Ud8^aQy>wy=$)w;JZzA)_*Y$Z5hU=KAG&htLw1Uh00yE!|Nu{EZkch zY9O6x7Y??>!7pUNME*d!=R#s)ghr|R#41l!c?~=3CS8&zr6*aA7n9*)*PWBV2w+&I zpW1-9fr3j{VTcls1>ua}F*bbju_Xq%^v;-W~paSqlf zolj*dt`BBjHI)H9{zrkBo=B%>8}4jeBO~kWqO!~Thi!I1H(in=n^fS%nuL=X2+s!p}HfTU#NBGiwEBF^^tKU zbhhv+0dE-sbK$>J#t-J!B$TMgN@Wh5wTtK2BG}4BGfsZOoRUS#G8Cxv|6EI*n&Xxq zt{&OxCC+BNqz$9b0WM7_PyBJEVObHFh%%`~!@MNZlo*oXDCwDcFwT~Rls!aApL<)^ zbBftGKKBRhB!{?fX@l2_y~%ygNFfF(XJzHh#?`WlSL{1lKT*gJM zs>bd^H9NCxqxn(IOky5k-wALFowQr(gw%|`0991u#9jXQh?4l|l>pd6a&rx|v=fPJ z1mutj{YzpJ_gsClbWFk(G}bSlFi-6@mwoQh-XeD*j@~huW4(8ub%^I|azA)h2t#yG z7e_V_<4jlM3D(I+qX}yEtqj)cpzN*oCdYHa!nm%0t^wHm)EmFP*|FMw!tb@&`G-u~ zK)=Sf6z+BiTAI}}i{*_Ac$ffr*Wrv$F7_0gJkjx;@)XjYSh`RjAgrCck`x!zP>Ifu z&%he4P|S)H*(9oB4uvH67^0}I-_ye_!w)u3v2+EY>eD3#8QR24<;7?*hj8k~rS)~7 zSXs5ww)T(0eHSp$hEIBnW|Iun<_i`}VE0Nc$|-R}wlSIs5pV{g_Dar(Zz<4X3`W?K z6&CAIl4U(Qk-tTcK{|zYF6QG5ArrEB!;5s?tW7 zrE3hcFY&k)+)e{+YOJ0X2uDE_hd2{|m_dC}kgEKqiE9Q^A-+>2UonB+L@v3$9?AYw zVQv?X*pK;X4Ovc6Ev5Gbg{{Eu*7{N3#0@9oMI~}KnObQE#Y{&3mM4`w%wN+xrKYgD zB-ay0Q}m{QI;iY`s1Z^NqIkjrTlf`B)B#MajZ#9u41oRBC1oM1vq0i|F59> z#StM@bHt|#`2)cpl_rWB($DNJ3Lap}QM-+A$3pe}NyP(@+i1>o^fe-oxX#Bt`mcQc zb?pD4W%#ep|3%CHAYnr*^M6Czg>~L4?l16H1OozM{P*en298b+`i4$|w$|4AHbzqB zHpYUsHZET$Z0ztC;U+0*+amF!@PI%^oUIZy{`L{%O^i{Xk}X0&nl)n~tVEpcAJSJ} zverw15zP1P-O8h9nd!&hj$zuwjg?DoxYIw{jWM zW5_pj+wFy8Tsa9g<7Qa21WaV&;ejoYflRKcz?#fSH_)@*QVlN2l4(QNk| z4aPnv&mrS&0|6NHq05XQw$J^RR9T{3SOcMKCXIR1iSf+xJ0E_Wv?jEc*I#ZPzyJN2 zUG0UOXHl+PikM*&g$U@g+KbG-RY>uaIl&DEtw_Q=FYq?etc!;hEC_}UX{eyh%dw2V zTTSlap&5>PY{6I#(6`j-9`D&I#|YPP8a;(sOzgeKDWsLa!i-$frD>zr-oid!Hf&yS z!i^cr&7tN}OOGmX2)`8k?Tn!!4=tz~3hCTq_9CdiV!NIblUDxHh(FJ$zs)B2(t5@u z-`^RA1ShrLCkg0)OhfoM;4Z{&oZmAec$qV@ zGQ(7(!CBk<5;Ar%DLJ0p0!ResC#U<+3i<|vib1?{5gCebG7$F7URKZXuX-2WgF>YJ^i zMhHDBsh9PDU8dlZ$yJKtc6JA#y!y$57%sE>4Nt+wF1lfNIWyA`=hF=9Gj%sRwi@vd z%2eVV3y&dvAgyuJ=eNJR+*080dbO_t@BFJO<@&#yqTK&+xc|FRR;p;KVk@J3$S{p` zGaMj6isho#%m)?pOG^G0mzOAw0z?!AEMsv=0T>WWcE>??WS=fII$t$(^PDPMU(P>o z_*0s^W#|x)%tx8jIgZY~A2yG;US0m2ZOQt6yJqW@XNY_>_R7(Nxb8Ged6BdYW6{prd!|zuX$@Q2o6Ona8zzYC1u!+2!Y$Jc9a;wy+pXt}o6~Bu1oF1c zp7Y|SBTNi@=I(K%A60PMjM#sfH$y*c{xUgeSpi#HB`?|`!Tb&-qJ3;vxS!TIzuTZs-&%#bAkAyw9m4PJgvey zM5?up*b}eDEY+#@tKec)-c(#QF0P?MRlD1+7%Yk*jW;)`f;0a-ZJ6CQA?E%>i2Dt7T9?s|9ZF|KP4;CNWvaVKZ+Qeut;Jith_y{v*Ny6Co6!8MZx;Wgo z=qAi%&S;8J{iyD&>3CLCQdTX*$+Rx1AwA*D_J^0>suTgBMBb=*hefV+Ars#mmr+YsI3#!F@Xc1t4F-gB@6aoyT+5O(qMz*zG<9Qq*f0w^V!03rpr*-WLH}; zfM{xSPJeu6D(%8HU%0GEa%waFHE$G?FH^kMS-&I3)ycx|iv{T6Wx}9$$D&6{%1N_8 z_CLw)_9+O4&u94##vI9b-HHm_95m)fa??q07`DniVjAy`t7;)4NpeyAY(aAk(+T_O z1om+b5K2g_B&b2DCTK<>SE$Ode1DopAi)xaJjU>**AJK3hZrnhEQ9E`2=|HHe<^tv z63e(bn#fMWuz>4erc47}!J>U58%<&N<6AOAewyzNTqi7hJc|X{782&cM zHZYclNbBwU6673=!ClmxMfkC$(CykGR@10F!zN1Se83LR&a~$Ht&>~43OX22mt7tcZUpa;9@q}KDX3O&Ugp6< zLZLfIMO5;pTee1vNyVC$FGxzK2f>0Z-6hM82zKg44nWo|n}$Zk6&;5ry3`(JFEX$q zK&KivAe${e^5ZGc3a9hOt|!UOE&OocpVryE$Y4sPcs4rJ>>Kbi2_subQ9($2VN(3o zb~tEzMsHaBmBtaHAyES+d3A(qURgiskSSwUc9CfJ@99&MKp2sooSYZu+-0t0+L*!I zYagjOlPgx|lep9tiU%ts&McF6b0VE57%E0Ho%2oi?=Ks+5%aj#au^OBwNwhec zta6QAeQI^V!dF1C)>RHAmB`HnxyqWx?td@4sd15zPd*Fc9hpDXP23kbBenBxGeD$k z;%0VBQEJ-C)&dTAw_yW@k0u?IUk*NrkJ)(XEeI z9Y>6Vel>#s_v@=@0<{4A{pl=9cQ&Iah0iD0H`q)7NeCIRz8zx;! z^OO;1+IqoQNak&pV`qKW+K0^Hqp!~gSohcyS)?^P`JNZXw@gc6{A3OLZ?@1Uc^I2v z+X!^R*HCm3{7JPq{8*Tn>5;B|X7n4QQ0Bs79uTU%nbqOJh`nX(BVj!#f;#J+WZxx4 z_yM&1Y`2XzhfqkIMO7tB3raJKQS+H5F%o83bM+hxbQ zeeJm=Dvix$2j|b4?mDacb67v-1^lTp${z=jc1=j~QD>7c*@+1?py>%Kj%Ejp7Y-!? z8iYRUlGVrQPandAaxFfks53@2EC#0)%mrnmGRn&>=$H$S8q|kE_iWko4`^vCS2aWg z#!`RHUGyOt*k?bBYu3*j3u0gB#v(3tsije zgIuNNWNtrOkx@Pzs;A9un+2LX!zw+p3_NX^Sh09HZAf>m8l@O*rXy_82aWT$Q>iyy zqO7Of)D=wcSn!0+467&!Hl))eff=$aneB?R!YykdKW@k^_uR!+Q1tR)+IJb`-6=jj zymzA>Sv4>Z&g&WWu#|~GcP7qP&m*w-S$)7Xr;(duqCTe7p8H3k5>Y-n8438+%^9~K z3r^LIT_K{i7DgEJjIocw_6d0!<;wKT`X;&vv+&msmhAAnIe!OTdybPctzcEzBy88_ zWO{6i4YT%e4^WQZB)KHCvA(0tS zHu_Bg+6Ko%a9~$EjRB90`P(2~6uI@SFibxct{H#o&y40MdiXblu@VFXbhz>Nko;7R z70Ntmm-FePqhb%9gL+7U8@(ch|JfH5Fm)5${8|`Lef>LttM_iww6LW2X61ldBmG0z zax3y)njFe>j*T{i0s8D4=L>X^j0)({R5lMGVS#7(2C9@AxL&C-lZQx~czI7Iv+{%1 z2hEG>RzX4S8x3v#9sgGAnPzptM)g&LB}@%E>fy0vGSa(&q0ch|=ncKjNrK z`jA~jObJhrJ^ri|-)J^HUyeZXz~XkBp$VhcTEcTdc#a2EUOGVX?@mYx#Vy*!qO$Jv zQ4rgOJ~M*o-_Wptam=~krnmG*p^j!JAqoQ%+YsDFW7Cc9M%YPiBOrVcD^RY>m9Pd< zu}#9M?K{+;UIO!D9qOpq9yxUquQRmQNMo0pT`@$pVt=rMvyX)ph(-CCJLvUJy71DI zBk7oc7)-%ngdj~s@76Yse3L^gV0 z2==qfp&Q~L(+%RHP0n}+xH#k(hPRx(!AdBM$JCfJ5*C=K3ts>P?@@SZ_+{U2qFZb>4kZ{Go37{# zSQc+-dq*a-Vy4?taS&{Ht|MLRiS)Sn14JOONyXqPNnpq&2y~)6wEG0oNy>qvod$FF z`9o&?&6uZjhZ4_*5qWVrEfu(>_n2Xi2{@Gz9MZ8!YmjYvIMasE9yVQL10NBrTCczq zcTY1q^PF2l!Eraguf{+PtHV3=2A?Cu&NN&a8V(y;q(^_mFc6)%Yfn&X&~Pq zU1?qCj^LF(EQB1F`8NxNjyV%fde}dEa(Hx=r7$~ts2dzDwyi6ByBAIx$NllB4%K=O z$AHz1<2bTUb>(MCVPpK(E9wlLElo(aSd(Os)^Raum`d(g9Vd_+Bf&V;l=@mM=cC>) z)9b0enb)u_7V!!E_bl>u5nf&Rl|2r=2F3rHMdb7y9E}}F82^$Rf+P8%dKnOeKh1vs zhH^P*4Ydr^$)$h@4KVzxrHyy#cKmWEa9P5DJ|- zG;!Qi35Tp7XNj60=$!S6U#!(${6hyh7d4q=pF{`0t|N^|L^d8pD{O9@tF~W;#Je*P z&ah%W!KOIN;SyAEhAeTafJ4uEL`(RtnovM+cb(O#>xQnk?dzAjG^~4$dFn^<@-Na3 z395;wBnS{t*H;Jef2eE!2}u5Ns{AHj>WYZDgQJt8v%x?9{MXqJsGP|l%OiZqQ1aB! z%E=*Ig`(!tHh>}4_z5IMpg{49UvD*Pp9!pxt_gdAW%sIf3k6CTycOT1McPl=_#0?8 zVjz8Hj*Vy9c5-krd-{BQ{6Xy|P$6LJvMuX$* zA+@I_66_ET5l2&gk9n4$1M3LN8(yEViRx&mtd#LD}AqEs?RW=xKC(OCWH;~>(X6h!uDxXIPH06xh z*`F4cVlbDP`A)-fzf>MuScYsmq&1LUMGaQ3bRm6i7OsJ|%uhTDT zlvZA1M}nz*SalJWNT|`dBm1$xlaA>CCiQ zK`xD-RuEn>-`Z?M{1%@wewf#8?F|(@1e0+T4>nmlSRrNK5f)BJ2H*$q(H>zGD0>eL zQ!tl_Wk)k*e6v^m*{~A;@6+JGeWU-q9>?+L_#UNT%G?4&BnOgvm9@o7l?ov~XL+et zbGT)|G7)KAeqb=wHSPk+J1bdg7N3$vp(ekjI1D9V$G5Cj!=R2w=3*4!z*J-r-cyeb zd(i2KmX!|Lhey!snRw z?#$Gu%S^SQEKt&kep)up#j&9}e+3=JJBS(s>MH+|=R(`8xK{mmndWo_r`-w1#SeRD&YtAJ#GiVI*TkQZ}&aq<+bU2+coU3!jCI6E+Ad_xFW*ghnZ$q zAoF*i&3n1j#?B8x;kjSJD${1jdRB;)R*)Ao!9bd|C7{;iqDo|T&>KSh6*hCD!rwv= zyK#F@2+cv3=|S1Kef(E6Niv8kyLVLX&e=U;{0x{$tDfShqkjUME>f8d(5nzSkY6@! z^-0>DM)wa&%m#UF1F?zR`8Y3X#tA!*7Q$P3lZJ%*KNlrk_uaPkxw~ zxZ1qlE;Zo;nb@!SMazSjM>;34ROOoygo%SF);LL>rRonWwR>bmSd1XD^~sGSu$Gg# zFZ`|yKU0%!v07dz^v(tY%;So(e`o{ZYTX`hm;@b0%8|H>VW`*cr8R%3n|ehw2`(9B+V72`>SY}9^8oh$En80mZK9T4abVG*to;E z1_S6bgDOW?!Oy1LwYy=w3q~KKdbNtyH#d24PFjX)KYMY93{3-mPP-H>@M-_>N~DDu zENh~reh?JBAK=TFN-SfDfT^=+{w4ea2KNWXq2Y<;?(gf(FgVp8Zp-oEjKzB%2Iqj;48GmY3h=bcdYJ}~&4tS`Q1sb=^emaW$IC$|R+r-8V- zf0$gGE(CS_n4s>oicVk)MfvVg#I>iDvf~Ov8bk}sSxluG!6#^Z_zhB&U^`eIi1@j( z^CK$z^stBHtaDDHxn+R;3u+>Lil^}fj?7eaGB z&5nl^STqcaBxI@v>%zG|j))G(rVa4aY=B@^2{TFkW~YP!8!9TG#(-nOf^^X-%m9{Z zCC?iC`G-^RcBSCuk=Z`(FaUUe?hf3{0C>>$?Vs z`2Uud9M+T&KB6o4o9kvdi^Q=Bw!asPdxbe#W-Oaa#_NP(qpyF@bVxv5D5))srkU#m zj_KA+#7sqDn*Ipf!F5Byco4HOSd!Ui$l94|IbW%Ny(s1>f4|Mv^#NfB31N~kya9!k zWCGL-$0ZQztBate^fd>R!hXY_N9ZjYp3V~4_V z#eB)Kjr8yW=+oG)BuNdZG?jaZlw+l_ma8aET(s+-x+=F-t#Qoiuu1i`^x8Sj>b^U} zs^z<()YMFP7CmjUC@M=&lA5W7t&cxTlzJAts*%PBDAPuqcV5o7HEnqjif_7xGt)F% zGx2b4w{@!tE)$p=l3&?Bf#`+!-RLOleeRk3 z7#pF|w@6_sBmn1nECqdunmG^}pr5(ZJQVvAt$6p3H(16~;vO>?sTE`Y+mq5YP&PBo zvq!7#W$Gewy`;%6o^!Dtjz~x)T}Bdk*BS#=EY=ODD&B=V6TD2z^hj1m5^d6s)D*wk zu$z~D7QuZ2b?5`p)E8e2_L38v3WE{V`bVk;6fl#o2`) z99JsWhh?$oVRn@$S#)uK&8DL8>An0&S<%V8hnGD7Z^;Y(%6;^9!7kDQ5bjR_V+~wp zfx4m3z6CWmmZ<8gDGUyg3>t8wgJ5NkkiEm^(sedCicP^&3D%}6LtIUq>mXCAt{9eF zNXL$kGcoUTf_Lhm`t;hD-SE)m=iBnxRU(NyL}f6~1uH)`K!hmYZjLI%H}AmEF5RZt z06$wn63GHnApHXZZJ}s^s)j9(BM6e*7IBK6Bq(!)d~zR#rbxK9NVIlgquoMq z=eGZ9NR!SEqP6=9UQg#@!rtbbSBUM#ynF);zKX+|!Zm}*{H z+j=d?aZ2!?@EL7C~%B?6ouCKLnO$uWn;Y6Xz zX8dSwj732u(o*U3F$F=7xwxm>E-B+SVZH;O-4XPuPkLSt_?S0)lb7EEg)Mglk0#eS z9@jl(OnH4juMxY+*r03VDfPx_IM!Lmc(5hOI;`?d37f>jPP$?9jQQIQU@i4vuG6MagEoJrQ=RD7xt@8E;c zeGV*+Pt+t$@pt!|McETOE$9k=_C!70uhwRS9X#b%ZK z%q(TIUXSS^F0`4Cx?Rk07C6wI4!UVPeI~-fxY6`YH$kABdOuiRtl73MqG|~AzZ@iL&^s?24iS;RK_pdlWkhcF z@Wv-Om(Aealfg)D^adlXh9Nvf~Uf@y;g3Y)i(YP zEXDnb1V}1pJT5ZWyw=1i+0fni9yINurD=EqH^ciOwLUGi)C%Da)tyt=zq2P7pV5-G zR7!oq28-Fgn5pW|nlu^b!S1Z#r7!Wtr{5J5PQ>pd+2P7RSD?>(U7-|Y z7ZQ5lhYIl_IF<9?T9^IPK<(Hp;l5bl5tF9>X-zG14_7PfsA>6<$~A338iYRT{a@r_ zuXBaT=`T5x3=s&3=RYx6NgG>No4?5KFBVjE(swfcivcIpPQFx5l+O;fiGsOrl5teR z_Cm+;PW}O0Dwe_(4Z@XZ)O0W-v2X><&L*<~*q3dg;bQW3g7)a#3KiQP>+qj|qo*Hk z?57>f2?f@`=Fj^nkDKeRkN2d$Z@2eNKpHo}ksj-$`QKb6n?*$^*%Fb3_Kbf1(*W9K>{L$mud2WHJ=j0^=g30Xhg8$#g^?36`p1fm;;1@0Lrx+8t`?vN0ZorM zSW?rhjCE8$C|@p^sXdx z|NOHHg+fL;HIlqyLp~SSdIF`TnSHehNCU9t89yr@)FY<~hu+X`tjg(aSVae$wDG*C zq$nY(Y494R)hD!i1|IIyP*&PD_c2FPgeY)&mX1qujB1VHPG9`yFQpLFVQ0>EKS@Bp zAfP5`C(sWGLI?AC{XEjLKR4FVNw(4+9b?kba95ukgR1H?w<8F7)G+6&(zUhIE5Ef% z=fFkL3QKA~M@h{nzjRq!Y_t!%U66#L8!(2-GgFxkD1=JRRqk=n%G(yHKn%^&$dW>; zSjAcjETMz1%205se$iH_)ZCpfg_LwvnsZQAUCS#^FExp8O4CrJb6>JquNV@qPq~3A zZ<6dOU#6|8+fcgiA#~MDmcpIEaUO02L5#T$HV0$EMD94HT_eXLZ2Zi&(! z&5E>%&|FZ`)CN10tM%tLSPD*~r#--K(H-CZqIOb99_;m|D5wdgJ<1iOJz@h2Zkq?} z%8_KXb&hf=2Wza(Wgc;3v3TN*;HTU*q2?#z&tLn_U0Nt!y>Oo>+2T)He6%XuP;fgn z-G!#h$Y2`9>Jtf}hbVrm6D70|ERzLAU>3zoWhJmjWfgM^))T+2u$~5>HF9jQDkrXR z=IzX36)V75PrFjkQ%TO+iqKGCQ-DDXbaE;C#}!-CoWQx&v*vHfyI>$HNRbpvm<`O( zlx9NBWD6_e&J%Ous4yp~s6)Ghni!I6)0W;9(9$y1wWu`$gs<$9Mcf$L*piP zPR0Av*2%ul`W;?-1_-5Zy0~}?`e@Y5A&0H!^ApyVTT}BiOm4GeFo$_oPlDEyeGBbh z1h3q&Dx~GmUS|3@4V36&$2uO8!Yp&^pD7J5&TN{?xphf*-js1fP?B|`>p_K>lh{ij zP(?H%e}AIP?_i^f&Li=FDSQ`2_NWxL+BB=nQr=$ zHojMlXNGauvvwPU>ZLq!`bX-5F4jBJ&So{kE5+ms9UEYD{66!|k~3vsP+mE}x!>%P za98bAU0!h0&ka4EoiDvBM#CP#dRNdXJcb*(%=<(g+M@<)DZ!@v1V>;54En?igcHR2 zhubQMq}VSOK)onqHfczM7YA@s=9*ow;k;8)&?J3@0JiGcP! zP#00KZ1t)GyZeRJ=f0^gc+58lc4Qh*S7RqPIC6GugG1gXe$LIQMRCo8cHf^qXgAa2 z`}t>u2Cq1CbSEpLr~E=c7~=Qkc9-vLE%(v9N*&HF`(d~(0`iukl5aQ9u4rUvc8%m) zr2GwZN4!s;{SB87lJB;veebPmqE}tSpT>+`t?<457Q9iV$th%i__Z1kOMAswFldD6 ztbOvO337S5o#ZZgN2G99_AVqPv!?Gmt3pzgD+Hp3QPQ`9qJ(g=kjvD+fUSS3upJn! zqoG7acIKEFRX~S}3|{EWT$kdz#zrDlJU(rPkxjws_iyLKU8+v|*oS_W*-guAb&Pj1 z35Z`3z<&Jb@2Mwz=KXucNYdY#SNO$tcVFr9KdKm|%^e-TXzs6M`PBper%ajkrIyUe zp$vVxVs9*>Vp4_1NC~Zg)WOCPmOxI1V34QlG4!aSFOH{QqSVq1^1)- z0P!Z?tT&E-ll(pwf0?=F=yOzik=@nh1Clxr9}Vij89z)ePDSCYAqw?lVI?v?+&*zH z)p$CScFI8rrwId~`}9YWPFu0cW1Sf@vRELs&cbntRU6QfPK-SO*mqu|u~}8AJ!Q$z znzu}50O=YbjwKCuSVBs6&CZR#0FTu)3{}qJJYX(>QPr4$RqWiwX3NT~;>cLn*_&1H zaKpIW)JVJ>b{uo2oq>oQt3y=zJjb%fU@wLqM{SyaC6x2snMx-}ivfU<1- znu1Lh;i$3Tf$Kh5Uk))G!D1UhE8pvx&nO~w^fG)BC&L!_hQk%^p`Kp@F{cz>80W&T ziOK=Sq3fdRu*V0=S53rcIfWFazI}Twj63CG(jOB;$*b`*#B9uEnBM`hDk*EwSRdwP8?5T?xGUKs=5N83XsR*)a4|ijz|c{4tIU+4j^A5C<#5 z*$c_d=5ml~%pGxw#?*q9N7aRwPux5EyqHVkdJO=5J>84!X6P>DS8PTTz>7C#FO?k#edkntG+fJk8ZMn?pmJSO@`x-QHq;7^h6GEXLXo1TCNhH z8ZDH{*NLAjo3WM`xeb=X{((uv3H(8&r8fJJg_uSs_%hOH%JDD?hu*2NvWGYD+j)&` zz#_1%O1wF^o5ryt?O0n;`lHbzp0wQ?rcbW(F1+h7_EZZ9{>rePvLAPVZ_R|n@;b$;UchU=0j<6k8G9QuQf@76oiE*4 zXOLQ&n3$NR#p4<5NJMVC*S);5x2)eRbaAM%VxWu9ohlT;pGEk7;002enCbQ>2r-us z3#bpXP9g|mE`65VrN`+3mC)M(eMj~~eOf)do<@l+fMiTR)XO}422*1SL{wyY(%oMpBgJagtiDf zz>O6(m;};>Hi=t8o{DVC@YigqS(Qh+ix3Rwa9aliH}a}IlOCW1@?%h_bRbq-W{KHF z%Vo?-j@{Xi@=~Lz5uZP27==UGE15|g^0gzD|3x)SCEXrx`*MP^FDLl%pOi~~Il;dc z^hrwp9sYeT7iZ)-ajKy@{a`kr0-5*_!XfBpXwEcFGJ;%kV$0Nx;apKrur zJN2J~CAv{Zjj%FolyurtW8RaFmpn&zKJWL>(0;;+q(%(Hx!GMW4AcfP0YJ*Vz!F4g z!ZhMyj$BdXL@MlF%KeInmPCt~9&A!;cRw)W!Hi@0DY(GD_f?jeV{=s=cJ6e}JktJw zQORnxxj3mBxfrH=x{`_^Z1ddDh}L#V7i}$njUFRVwOX?qOTKjfPMBO4y(WiU<)epb zvB9L=%jW#*SL|Nd_G?E*_h1^M-$PG6Pc_&QqF0O-FIOpa4)PAEPsyvB)GKasmBoEt z?_Q2~QCYGH+hW31x-B=@5_AN870vY#KB~3a*&{I=f);3Kv7q4Q7s)0)gVYx2#Iz9g(F2;=+Iy4 z6KI^8GJ6D@%tpS^8boU}zpi=+(5GfIR)35PzrbuXeL1Y1N%JK7PG|^2k3qIqHfX;G zQ}~JZ-UWx|60P5?d1e;AHx!_;#PG%d=^X(AR%i`l0jSpYOpXoKFW~7ip7|xvN;2^? zsYC9fanpO7rO=V7+KXqVc;Q5z%Bj})xHVrgoR04sA2 zl~DAwv=!(()DvH*=lyhIlU^hBkA0$e*7&fJpB0|oB7)rqGK#5##2T`@_I^|O2x4GO z;xh6ROcV<9>?e0)MI(y++$-ksV;G;Xe`lh76T#Htuia+(UrIXrf9?

L(tZ$0BqX1>24?V$S+&kLZ`AodQ4_)P#Q3*4xg8}lMV-FLwC*cN$< zt65Rf%7z41u^i=P*qO8>JqXPrinQFapR7qHAtp~&RZ85$>ob|Js;GS^y;S{XnGiBc zGa4IGvDl?x%gY`vNhv8wgZnP#UYI-w*^4YCZnxkF85@ldepk$&$#3EAhrJY0U)lR{F6sM3SONV^+$;Zx8BD&Eku3K zKNLZyBni3)pGzU0;n(X@1fX8wYGKYMpLmCu{N5-}epPDxClPFK#A@02WM3!myN%bkF z|GJ4GZ}3sL{3{qXemy+#Uk{4>Kf8v11;f8I&c76+B&AQ8udd<8gU7+BeWC`akUU~U zgXoxie>MS@rBoyY8O8Tc&8id!w+_ooxcr!1?#rc$-|SBBtH6S?)1e#P#S?jFZ8u-Bs&k`yLqW|{j+%c#A4AQ>+tj$Y z^CZajspu$F%73E68Lw5q7IVREED9r1Ijsg#@DzH>wKseye>hjsk^{n0g?3+gs@7`i zHx+-!sjLx^fS;fY!ERBU+Q zVJ!e0hJH%P)z!y%1^ZyG0>PN@5W~SV%f>}c?$H8r;Sy-ui>aruVTY=bHe}$e zi&Q4&XK!qT7-XjCrDaufT@>ieQ&4G(SShUob0Q>Gznep9fR783jGuUynAqc6$pYX; z7*O@@JW>O6lKIk0G00xsm|=*UVTQBB`u1f=6wGAj%nHK_;Aqmfa!eAykDmi-@u%6~ z;*c!pS1@V8r@IX9j&rW&d*}wpNs96O2Ute>%yt{yv>k!6zfT6pru{F1M3P z2WN1JDYqoTB#(`kE{H676QOoX`cnqHl1Yaru)>8Ky~VU{)r#{&s86Vz5X)v15ULHA zAZDb{99+s~qI6;-dQ5DBjHJP@GYTwn;Dv&9kE<0R!d z8tf1oq$kO`_sV(NHOSbMwr=To4r^X$`sBW4$gWUov|WY?xccQJN}1DOL|GEaD_!@& z15p?Pj+>7d`@LvNIu9*^hPN)pwcv|akvYYq)ks%`G>!+!pW{-iXPZsRp8 z35LR;DhseQKWYSD`%gO&k$Dj6_6q#vjWA}rZcWtQr=Xn*)kJ9kacA=esi*I<)1>w^ zO_+E>QvjP)qiSZg9M|GNeLtO2D7xT6vsj`88sd!94j^AqxFLi}@w9!Y*?nwWARE0P znuI_7A-saQ+%?MFA$gttMV-NAR^#tjl_e{R$N8t2NbOlX373>e7Ox=l=;y#;M7asp zRCz*CLnrm$esvSb5{T<$6CjY zmZ(i{Rs_<#pWW>(HPaaYj`%YqBra=Ey3R21O7vUbzOkJJO?V`4-D*u4$Me0Bx$K(lYo`JO}gnC zx`V}a7m-hLU9Xvb@K2ymioF)vj12<*^oAqRuG_4u%(ah?+go%$kOpfb`T96P+L$4> zQ#S+sA%VbH&mD1k5Ak7^^dZoC>`1L%i>ZXmooA!%GI)b+$D&ziKrb)a=-ds9xk#~& z7)3iem6I|r5+ZrTRe_W861x8JpD`DDIYZNm{$baw+$)X^Jtjnl0xlBgdnNY}x%5za zkQ8E6T<^$sKBPtL4(1zi_Rd(tVth*3Xs!ulflX+70?gb&jRTnI8l+*Aj9{|d%qLZ+ z>~V9Z;)`8-lds*Zgs~z1?Fg?Po7|FDl(Ce<*c^2=lFQ~ahwh6rqSjtM5+$GT>3WZW zj;u~w9xwAhOc<kF}~`CJ68 z?(S5vNJa;kriPlim33{N5`C{9?NWhzsna_~^|K2k4xz1`xcui*LXL-1#Y}Hi9`Oo!zQ>x-kgAX4LrPz63uZ+?uG*84@PKq-KgQlMNRwz=6Yes) zY}>YN+qP}nwr$(CZQFjUOI=-6J$2^XGvC~EZ+vrqWaOXB$k?%Suf5k=4>AveC1aJ! ziaW4IS%F$_Babi)kA8Y&u4F7E%99OPtm=vzw$$ zEz#9rvn`Iot_z-r3MtV>k)YvErZ<^Oa${`2>MYYODSr6?QZu+be-~MBjwPGdMvGd!b!elsdi4% z`37W*8+OGulab8YM?`KjJ8e+jM(tqLKSS@=jimq3)Ea2EB%88L8CaM+aG7;27b?5` z4zuUWBr)f)k2o&xg{iZ$IQkJ+SK>lpq4GEacu~eOW4yNFLU!Kgc{w4&D$4ecm0f}~ zTTzquRW@`f0}|IILl`!1P+;69g^upiPA6F{)U8)muWHzexRenBU$E^9X-uIY2%&1w z_=#5*(nmxJ9zF%styBwivi)?#KMG96-H@hD-H_&EZiRNsfk7mjBq{L%!E;Sqn!mVX*}kXhwH6eh;b42eD!*~upVG@ z#smUqz$ICm!Y8wY53gJeS|Iuard0=;k5i5Z_hSIs6tr)R4n*r*rE`>38Pw&lkv{_r!jNN=;#?WbMj|l>cU(9trCq; z%nN~r^y7!kH^GPOf3R}?dDhO=v^3BeP5hF|%4GNQYBSwz;x({21i4OQY->1G=KFyu z&6d`f2tT9Yl_Z8YACZaJ#v#-(gcyeqXMhYGXb=t>)M@fFa8tHp2x;ODX=Ap@a5I=U z0G80^$N0G4=U(>W%mrrThl0DjyQ-_I>+1Tdd_AuB3qpYAqY54upwa3}owa|x5iQ^1 zEf|iTZxKNGRpI>34EwkIQ2zHDEZ=(J@lRaOH>F|2Z%V_t56Km$PUYu^xA5#5Uj4I4RGqHD56xT%H{+P8Ag>e_3pN$4m8n>i%OyJFPNWaEnJ4McUZPa1QmOh?t8~n& z&RulPCors8wUaqMHECG=IhB(-tU2XvHP6#NrLVyKG%Ee*mQ5Ps%wW?mcnriTVRc4J`2YVM>$ixSF2Xi+Wn(RUZnV?mJ?GRdw%lhZ+t&3s7g!~g{%m&i<6 z5{ib-<==DYG93I(yhyv4jp*y3#*WNuDUf6`vTM%c&hiayf(%=x@4$kJ!W4MtYcE#1 zHM?3xw63;L%x3drtd?jot!8u3qeqctceX3m;tWetK+>~q7Be$h>n6riK(5@ujLgRS zvOym)k+VAtyV^mF)$29Y`nw&ijdg~jYpkx%*^ z8dz`C*g=I?;clyi5|!27e2AuSa$&%UyR(J3W!A=ZgHF9OuKA34I-1U~pyD!KuRkjA zbkN!?MfQOeN>DUPBxoy5IX}@vw`EEB->q!)8fRl_mqUVuRu|C@KD-;yl=yKc=ZT0% zB$fMwcC|HE*0f8+PVlWHi>M`zfsA(NQFET?LrM^pPcw`cK+Mo0%8*x8@65=CS_^$cG{GZQ#xv($7J z??R$P)nPLodI;P!IC3eEYEHh7TV@opr#*)6A-;EU2XuogHvC;;k1aI8asq7ovoP!* z?x%UoPrZjj<&&aWpsbr>J$Er-7!E(BmOyEv!-mbGQGeJm-U2J>74>o5x`1l;)+P&~ z>}f^=Rx(ZQ2bm+YE0u=ZYrAV@apyt=v1wb?R@`i_g64YyAwcOUl=C!i>=Lzb$`tjv zOO-P#A+)t-JbbotGMT}arNhJmmGl-lyUpMn=2UacVZxmiG!s!6H39@~&uVokS zG=5qWhfW-WOI9g4!R$n7!|ViL!|v3G?GN6HR0Pt_L5*>D#FEj5wM1DScz4Jv@Sxnl zB@MPPmdI{(2D?;*wd>3#tjAirmUnQoZrVv`xM3hARuJksF(Q)wd4P$88fGYOT1p6U z`AHSN!`St}}UMBT9o7i|G`r$ zrB=s$qV3d6$W9@?L!pl0lf%)xs%1ko^=QY$ty-57=55PvP(^6E7cc zGJ*>m2=;fOj?F~yBf@K@9qwX0hA803Xw+b0m}+#a(>RyR8}*Y<4b+kpp|OS+!whP( zH`v{%s>jsQI9rd$*vm)EkwOm#W_-rLTHcZRek)>AtF+~<(did)*oR1|&~1|e36d-d zgtm5cv1O0oqgWC%Et@P4Vhm}Ndl(Y#C^MD03g#PH-TFy+7!Osv1z^UWS9@%JhswEq~6kSr2DITo59+; ze=ZC}i2Q?CJ~Iyu?vn|=9iKV>4j8KbxhE4&!@SQ^dVa-gK@YfS9xT(0kpW*EDjYUkoj! zE49{7H&E}k%5(>sM4uGY)Q*&3>{aitqdNnRJkbOmD5Mp5rv-hxzOn80QsG=HJ_atI-EaP69cacR)Uvh{G5dTpYG7d zbtmRMq@Sexey)||UpnZ?;g_KMZq4IDCy5}@u!5&B^-=6yyY{}e4Hh3ee!ZWtL*s?G zxG(A!<9o!CL+q?u_utltPMk+hn?N2@?}xU0KlYg?Jco{Yf@|mSGC<(Zj^yHCvhmyx z?OxOYoxbptDK()tsJ42VzXdINAMWL$0Gcw?G(g8TMB)Khw_|v9`_ql#pRd2i*?CZl z7k1b!jQB=9-V@h%;Cnl7EKi;Y^&NhU0mWEcj8B|3L30Ku#-9389Q+(Yet0r$F=+3p z6AKOMAIi|OHyzlHZtOm73}|ntKtFaXF2Fy|M!gOh^L4^62kGUoWS1i{9gsds_GWBc zLw|TaLP64z3z9?=R2|T6Xh2W4_F*$cq>MtXMOy&=IPIJ`;!Tw?PqvI2b*U1)25^<2 zU_ZPoxg_V0tngA0J+mm?3;OYw{i2Zb4x}NedZug!>EoN3DC{1i)Z{Z4m*(y{ov2%- zk(w>+scOO}MN!exSc`TN)!B=NUX`zThWO~M*ohqq;J2hx9h9}|s#?@eR!=F{QTrq~ zTcY|>azkCe$|Q0XFUdpFT=lTcyW##i;-e{}ORB4D?t@SfqGo_cS z->?^rh$<&n9DL!CF+h?LMZRi)qju!meugvxX*&jfD!^1XB3?E?HnwHP8$;uX{Rvp# zh|)hM>XDv$ZGg=$1{+_bA~u-vXqlw6NH=nkpyWE0u}LQjF-3NhATL@9rRxMnpO%f7 z)EhZf{PF|mKIMFxnC?*78(}{Y)}iztV12}_OXffJ;ta!fcFIVjdchyHxH=t%ci`Xd zX2AUB?%?poD6Zv*&BA!6c5S#|xn~DK01#XvjT!w!;&`lDXSJT4_j$}!qSPrb37vc{ z9^NfC%QvPu@vlxaZ;mIbn-VHA6miwi8qJ~V;pTZkKqqOii<1Cs}0i?uUIss;hM4dKq^1O35y?Yp=l4i zf{M!@QHH~rJ&X~8uATV><23zZUbs-J^3}$IvV_ANLS08>k`Td7aU_S1sLsfi*C-m1 z-e#S%UGs4E!;CeBT@9}aaI)qR-6NU@kvS#0r`g&UWg?fC7|b^_HyCE!8}nyh^~o@< zpm7PDFs9yxp+byMS(JWm$NeL?DNrMCNE!I^ko-*csB+dsf4GAq{=6sfyf4wb>?v1v zmb`F*bN1KUx-`ra1+TJ37bXNP%`-Fd`vVQFTwWpX@;s(%nDQa#oWhgk#mYlY*!d>( zE&!|ySF!mIyfING+#%RDY3IBH_fW$}6~1%!G`suHub1kP@&DoAd5~7J55;5_noPI6eLf{t;@9Kf<{aO0`1WNKd?<)C-|?C?)3s z>wEq@8=I$Wc~Mt$o;g++5qR+(6wt9GI~pyrDJ%c?gPZe)owvy^J2S=+M^ z&WhIE`g;;J^xQLVeCtf7b%Dg#Z2gq9hp_%g)-%_`y*zb; zn9`f`mUPN-Ts&fFo(aNTsXPA|J!TJ{0hZp0^;MYHLOcD=r_~~^ymS8KLCSeU3;^QzJNqS z5{5rEAv#l(X?bvwxpU;2%pQftF`YFgrD1jt2^~Mt^~G>T*}A$yZc@(k9orlCGv&|1 zWWvVgiJsCAtamuAYT~nzs?TQFt<1LSEx!@e0~@yd6$b5!Zm(FpBl;(Cn>2vF?k zOm#TTjFwd2D-CyA!mqR^?#Uwm{NBemP>(pHmM}9;;8`c&+_o3#E5m)JzfwN?(f-a4 zyd%xZc^oQx3XT?vcCqCX&Qrk~nu;fxs@JUoyVoi5fqpi&bUhQ2y!Ok2pzsFR(M(|U zw3E+kH_zmTRQ9dUMZWRE%Zakiwc+lgv7Z%|YO9YxAy`y28`Aw;WU6HXBgU7fl@dnt z-fFBV)}H-gqP!1;V@Je$WcbYre|dRdp{xt!7sL3Eoa%IA`5CAA%;Wq8PktwPdULo! z8!sB}Qt8#jH9Sh}QiUtEPZ6H0b*7qEKGJ%ITZ|vH)5Q^2m<7o3#Z>AKc%z7_u`rXA zqrCy{-{8;9>dfllLu$^M5L z-hXs))h*qz%~ActwkIA(qOVBZl2v4lwbM>9l70Y`+T*elINFqt#>OaVWoja8RMsep z6Or3f=oBnA3vDbn*+HNZP?8LsH2MY)x%c13@(XfuGR}R?Nu<|07{$+Lc3$Uv^I!MQ z>6qWgd-=aG2Y^24g4{Bw9ueOR)(9h`scImD=86dD+MnSN4$6 z^U*o_mE-6Rk~Dp!ANp#5RE9n*LG(Vg`1)g6!(XtDzsov$Dvz|Gv1WU68J$CkshQhS zCrc|cdkW~UK}5NeaWj^F4MSgFM+@fJd{|LLM)}_O<{rj z+?*Lm?owq?IzC%U%9EBga~h-cJbIu=#C}XuWN>OLrc%M@Gu~kFEYUi4EC6l#PR2JS zQUkGKrrS#6H7}2l0F@S11DP`@pih0WRkRJl#F;u{c&ZC{^$Z+_*lB)r)-bPgRFE;* zl)@hK4`tEP=P=il02x7-C7p%l=B`vkYjw?YhdJU9!P!jcmY$OtC^12w?vy3<<=tlY zUwHJ_0lgWN9vf>1%WACBD{UT)1qHQSE2%z|JHvP{#INr13jM}oYv_5#xsnv9`)UAO zuwgyV4YZ;O)eSc3(mka6=aRohi!HH@I#xq7kng?Acdg7S4vDJb6cI5fw?2z%3yR+| zU5v@Hm}vy;${cBp&@D=HQ9j7NcFaOYL zj-wV=eYF{|XTkFNM2uz&T8uH~;)^Zo!=KP)EVyH6s9l1~4m}N%XzPpduPg|h-&lL` zAXspR0YMOKd2yO)eMFFJ4?sQ&!`dF&!|niH*!^*Ml##o0M(0*uK9&yzekFi$+mP9s z>W9d%Jb)PtVi&-Ha!o~Iyh@KRuKpQ@)I~L*d`{O8!kRObjO7=n+Gp36fe!66neh+7 zW*l^0tTKjLLzr`x4`_8&on?mjW-PzheTNox8Hg7Nt@*SbE-%kP2hWYmHu#Fn@Q^J(SsPUz*|EgOoZ6byg3ew88UGdZ>9B2Tq=jF72ZaR=4u%1A6Vm{O#?@dD!(#tmR;eP(Fu z{$0O%=Vmua7=Gjr8nY%>ul?w=FJ76O2js&17W_iq2*tb!i{pt#`qZB#im9Rl>?t?0c zicIC}et_4d+CpVPx)i4~$u6N-QX3H77ez z?ZdvXifFk|*F8~L(W$OWM~r`pSk5}#F?j_5u$Obu9lDWIknO^AGu+Blk7!9Sb;NjS zncZA?qtASdNtzQ>z7N871IsPAk^CC?iIL}+{K|F@BuG2>qQ;_RUYV#>hHO(HUPpk@ z(bn~4|F_jiZi}Sad;_7`#4}EmD<1EiIxa48QjUuR?rC}^HRocq`OQPM@aHVKP9E#q zy%6bmHygCpIddPjE}q_DPC`VH_2m;Eey&ZH)E6xGeStOK7H)#+9y!%-Hm|QF6w#A( zIC0Yw%9j$s-#odxG~C*^MZ?M<+&WJ+@?B_QPUyTg9DJGtQN#NIC&-XddRsf3n^AL6 zT@P|H;PvN;ZpL0iv$bRb7|J{0o!Hq+S>_NrH4@coZtBJu#g8#CbR7|#?6uxi8d+$g z87apN>EciJZ`%Zv2**_uiET9Vk{pny&My;+WfGDw4EVL#B!Wiw&M|A8f1A@ z(yFQS6jfbH{b8Z-S7D2?Ixl`j0{+ZnpT=;KzVMLW{B$`N?Gw^Fl0H6lT61%T2AU**!sX0u?|I(yoy&Xveg7XBL&+>n6jd1##6d>TxE*Vj=8lWiG$4=u{1UbAa5QD>5_ z;Te^42v7K6Mmu4IWT6Rnm>oxrl~b<~^e3vbj-GCdHLIB_>59}Ya+~OF68NiH=?}2o zP(X7EN=quQn&)fK>M&kqF|<_*H`}c zk=+x)GU>{Af#vx&s?`UKUsz})g^Pc&?Ka@t5$n$bqf6{r1>#mWx6Ep>9|A}VmWRnowVo`OyCr^fHsf# zQjQ3Ttp7y#iQY8l`zEUW)(@gGQdt(~rkxlkefskT(t%@i8=|p1Y9Dc5bc+z#n$s13 zGJk|V0+&Ekh(F};PJzQKKo+FG@KV8a<$gmNSD;7rd_nRdc%?9)p!|B-@P~kxQG}~B zi|{0}@}zKC(rlFUYp*dO1RuvPC^DQOkX4<+EwvBAC{IZQdYxoq1Za!MW7%p7gGr=j zzWnAq%)^O2$eItftC#TTSArUyL$U54-O7e|)4_7%Q^2tZ^0-d&3J1}qCzR4dWX!)4 zzIEKjgnYgMus^>6uw4Jm8ga6>GBtMjpNRJ6CP~W=37~||gMo_p@GA@#-3)+cVYnU> zE5=Y4kzl+EbEh%dhQokB{gqNDqx%5*qBusWV%!iprn$S!;oN_6E3?0+umADVs4ako z?P+t?m?};gev9JXQ#Q&KBpzkHPde_CGu-y z<{}RRAx=xlv#mVi+Ibrgx~ujW$h{?zPfhz)Kp7kmYS&_|97b&H&1;J-mzrBWAvY} zh8-I8hl_RK2+nnf&}!W0P+>5?#?7>npshe<1~&l_xqKd0_>dl_^RMRq@-Myz&|TKZBj1=Q()) zF{dBjv5)h=&Z)Aevx}+i|7=R9rG^Di!sa)sZCl&ctX4&LScQ-kMncgO(9o6W6)yd< z@Rk!vkja*X_N3H=BavGoR0@u0<}m-7|2v!0+2h~S2Q&a=lTH91OJsvms2MT~ zY=c@LO5i`mLpBd(vh|)I&^A3TQLtr>w=zoyzTd=^f@TPu&+*2MtqE$Avf>l>}V|3-8Fp2hzo3y<)hr_|NO(&oSD z!vEjTWBxbKTiShVl-U{n*B3#)3a8$`{~Pk}J@elZ=>Pqp|MQ}jrGv7KrNcjW%TN_< zZz8kG{#}XoeWf7qY?D)L)8?Q-b@Na&>i=)(@uNo zr;cH98T3$Iau8Hn*@vXi{A@YehxDE2zX~o+RY`)6-X{8~hMpc#C`|8y> zU8Mnv5A0dNCf{Ims*|l-^ z(MRp{qoGohB34|ggDI*p!Aw|MFyJ|v+<+E3brfrI)|+l3W~CQLPbnF@G0)P~Ly!1TJLp}xh8uW`Q+RB-v`MRYZ9Gam3cM%{ zb4Cb*f)0deR~wtNb*8w-LlIF>kc7DAv>T0D(a3@l`k4TFnrO+g9XH7;nYOHxjc4lq zMmaW6qpgAgy)MckYMhl?>sq;-1E)-1llUneeA!ya9KM$)DaNGu57Z5aE>=VST$#vb zFo=uRHr$0M{-ha>h(D_boS4zId;3B|Tpqo|?B?Z@I?G(?&Iei+-{9L_A9=h=Qfn-U z1wIUnQe9!z%_j$F_{rf&`ZFSott09gY~qrf@g3O=Y>vzAnXCyL!@(BqWa)Zqt!#_k zfZHuwS52|&&)aK;CHq9V-t9qt0au{$#6c*R#e5n3rje0hic7c7m{kW$p(_`wB=Gw7 z4k`1Hi;Mc@yA7dp@r~?@rfw)TkjAW++|pkfOG}0N|2guek}j8Zen(!+@7?qt_7ndX zB=BG6WJ31#F3#Vk3=aQr8T)3`{=p9nBHlKzE0I@v`{vJ}h8pd6vby&VgFhzH|q;=aonunAXL6G2y(X^CtAhWr*jI zGjpY@raZDQkg*aMq}Ni6cRF z{oWv}5`nhSAv>usX}m^GHt`f(t8@zHc?K|y5Zi=4G*UG1Sza{$Dpj%X8 zzEXaKT5N6F5j4J|w#qlZP!zS7BT)9b+!ZSJdToqJts1c!)fwih4d31vfb{}W)EgcA zH2pZ^8_k$9+WD2n`6q5XbOy8>3pcYH9 z07eUB+p}YD@AH!}p!iKv><2QF-Y^&xx^PAc1F13A{nUeCDg&{hnix#FiO!fe(^&%Qcux!h znu*S!s$&nnkeotYsDthh1dq(iQrE|#f_=xVgfiiL&-5eAcC-> z5L0l|DVEM$#ulf{bj+Y~7iD)j<~O8CYM8GW)dQGq)!mck)FqoL^X zwNdZb3->hFrbHFm?hLvut-*uK?zXn3q1z|UX{RZ;-WiLoOjnle!xs+W0-8D)kjU#R z+S|A^HkRg$Ij%N4v~k`jyHffKaC~=wg=9)V5h=|kLQ@;^W!o2^K+xG&2n`XCd>OY5Ydi= zgHH=lgy++erK8&+YeTl7VNyVm9-GfONlSlVb3)V9NW5tT!cJ8d7X)!b-$fb!s76{t z@d=Vg-5K_sqHA@Zx-L_}wVnc@L@GL9_K~Zl(h5@AR#FAiKad8~KeWCo@mgXIQ#~u{ zgYFwNz}2b6Vu@CP0XoqJ+dm8px(5W5-Jpis97F`+KM)TuP*X8H@zwiVKDKGVp59pI zifNHZr|B+PG|7|Y<*tqap0CvG7tbR1R>jn70t1X`XJixiMVcHf%Ez*=xm1(CrTSDt z0cle!+{8*Ja&EOZ4@$qhBuKQ$U95Q%rc7tg$VRhk?3=pE&n+T3upZg^ZJc9~c2es% zh7>+|mrmA-p&v}|OtxqmHIBgUxL~^0+cpfkSK2mhh+4b=^F1Xgd2)}U*Yp+H?ls#z zrLxWg_hm}AfK2XYWr!rzW4g;+^^&bW%LmbtRai9f3PjU${r@n`JThy-cphbcwn)rq9{A$Ht`lmYKxOacy z6v2R(?gHhD5@&kB-Eg?4!hAoD7~(h>(R!s1c1Hx#s9vGPePUR|of32bS`J5U5w{F) z>0<^ktO2UHg<0{oxkdOQ;}coZDQph8p6ruj*_?uqURCMTac;>T#v+l1Tc~%^k-Vd@ zkc5y35jVNc49vZpZx;gG$h{%yslDI%Lqga1&&;mN{Ush1c7p>7e-(zp}6E7f-XmJb4nhk zb8zS+{IVbL$QVF8pf8}~kQ|dHJAEATmmnrb_wLG}-yHe>W|A&Y|;muy-d^t^<&)g5SJfaTH@P1%euONny=mxo+C z4N&w#biWY41r8k~468tvuYVh&XN&d#%QtIf9;iVXfWY)#j=l`&B~lqDT@28+Y!0E+MkfC}}H*#(WKKdJJq=O$vNYCb(ZG@p{fJgu;h z21oHQ(14?LeT>n5)s;uD@5&ohU!@wX8w*lB6i@GEH0pM>YTG+RAIWZD;4#F1&F%Jp zXZUml2sH0!lYJT?&sA!qwez6cXzJEd(1ZC~kT5kZSp7(@=H2$Azb_*W&6aA|9iwCL zdX7Q=42;@dspHDwYE?miGX#L^3xD&%BI&fN9^;`v4OjQXPBaBmOF1;#C)8XA(WFlH zycro;DS2?(G&6wkr6rqC>rqDv3nfGw3hmN_9Al>TgvmGsL8_hXx09};l9Ow@)F5@y z#VH5WigLDwZE4nh^7&@g{1FV^UZ%_LJ-s<{HN*2R$OPg@R~Z`c-ET*2}XB@9xvAjrK&hS=f|R8Gr9 zr|0TGOsI7RD+4+2{ZiwdVD@2zmg~g@^D--YL;6UYGSM8i$NbQr4!c7T9rg!8;TM0E zT#@?&S=t>GQm)*ua|?TLT2ktj#`|R<_*FAkOu2Pz$wEc%-=Y9V*$&dg+wIei3b*O8 z2|m$!jJG!J!ZGbbIa!(Af~oSyZV+~M1qGvelMzPNE_%5?c2>;MeeG2^N?JDKjFYCy z7SbPWH-$cWF9~fX%9~v99L!G(wi!PFp>rB!9xj7=Cv|F+7CsGNwY0Q_J%FID%C^CBZQfJ9K(HK%k31j~e#&?hQ zNuD6gRkVckU)v+53-fc} z7ZCzYN-5RG4H7;>>Hg?LU9&5_aua?A0)0dpew1#MMlu)LHe(M;OHjHIUl7|%%)YPo z0cBk;AOY00%Fe6heoN*$(b<)Cd#^8Iu;-2v@>cE-OB$icUF9EEoaC&q8z9}jMTT2I z8`9;jT%z0;dy4!8U;GW{i`)3!c6&oWY`J3669C!tM<5nQFFrFRglU8f)5Op$GtR-3 zn!+SPCw|04sv?%YZ(a7#L?vsdr7ss@WKAw&A*}-1S|9~cL%uA+E~>N6QklFE>8W|% zyX-qAUGTY1hQ-+um`2|&ji0cY*(qN!zp{YpDO-r>jPk*yuVSay<)cUt`t@&FPF_&$ zcHwu1(SQ`I-l8~vYyUxm@D1UEdFJ$f5Sw^HPH7b!9 zzYT3gKMF((N(v0#4f_jPfVZ=ApN^jQJe-X$`A?X+vWjLn_%31KXE*}5_}d8 zw_B1+a#6T1?>M{ronLbHIlEsMf93muJ7AH5h%;i99<~JX^;EAgEB1uHralD*!aJ@F zV2ruuFe9i2Q1C?^^kmVy921eb=tLDD43@-AgL^rQ3IO9%+vi_&R2^dpr}x{bCVPej z7G0-0o64uyWNtr*loIvslyo0%)KSDDKjfThe0hcqs)(C-MH1>bNGBDRTW~scy_{w} zp^aq8Qb!h9Lwielq%C1b8=?Z=&U)ST&PHbS)8Xzjh2DF?d{iAv)Eh)wsUnf>UtXN( zL7=$%YrZ#|^c{MYmhn!zV#t*(jdmYdCpwqpZ{v&L8KIuKn`@IIZfp!uo}c;7J57N` zAxyZ-uA4=Gzl~Ovycz%MW9ZL7N+nRo&1cfNn9(1H5eM;V_4Z_qVann7F>5f>%{rf= zPBZFaV@_Sobl?Fy&KXyzFDV*FIdhS5`Uc~S^Gjo)aiTHgn#<0C=9o-a-}@}xDor;D zZyZ|fvf;+=3MZd>SR1F^F`RJEZo+|MdyJYQAEauKu%WDol~ayrGU3zzbHKsnHKZ*z zFiwUkL@DZ>!*x05ql&EBq@_Vqv83&?@~q5?lVmffQZ+V-=qL+!u4Xs2Z2zdCQ3U7B&QR9_Iggy} z(om{Y9eU;IPe`+p1ifLx-XWh?wI)xU9ik+m#g&pGdB5Bi<`PR*?92lE0+TkRuXI)z z5LP!N2+tTc%cB6B1F-!fj#}>S!vnpgVU~3!*U1ej^)vjUH4s-bd^%B=ItQqDCGbrEzNQi(dJ`J}-U=2{7-d zK8k^Rlq2N#0G?9&1?HSle2vlkj^KWSBYTwx`2?9TU_DX#J+f+qLiZCqY1TXHFxXZqYMuD@RU$TgcnCC{_(vwZ-*uX)~go#%PK z@}2Km_5aQ~(<3cXeJN6|F8X_1@L%@xTzs}$_*E|a^_URF_qcF;Pfhoe?FTFwvjm1o z8onf@OY@jC2tVcMaZS;|T!Ks(wOgPpRzRnFS-^RZ4E!9dsnj9sFt609a|jJbb1Dt@ z<=Gal2jDEupxUSwWu6zp<<&RnAA;d&4gKVG0iu6g(DsST(4)z6R)zDpfaQ}v{5ARt zyhwvMtF%b-YazR5XLz+oh=mn;y-Mf2a8>7?2v8qX;19y?b>Z5laGHvzH;Nu9S`B8} zI)qN$GbXIQ1VL3lnof^6TS~rvPVg4V?Dl2Bb*K2z4E{5vy<(@@K_cN@U>R!>aUIRnb zL*)=787*cs#zb31zBC49x$`=fkQbMAef)L2$dR{)6BAz!t5U_B#1zZG`^neKSS22oJ#5B=gl%U=WeqL9REF2g zZnfCb0?quf?Ztj$VXvDSWoK`0L=Zxem2q}!XWLoT-kYMOx)!7fcgT35uC~0pySEme z`{wGWTkGr7>+Kb^n;W?BZH6ZP(9tQX%-7zF>vc2}LuWDI(9kh1G#7B99r4x6;_-V+k&c{nPUrR zAXJGRiMe~aup{0qzmLNjS_BC4cB#sXjckx{%_c&^xy{M61xEb>KW_AG5VFXUOjAG4 z^>Qlm9A#1N{4snY=(AmWzatb!ngqiqPbBZ7>Uhb3)dTkSGcL#&SH>iMO-IJBPua`u zo)LWZ>=NZLr758j{%(|uQuZ)pXq_4c!!>s|aDM9#`~1bzK3J1^^D#<2bNCccH7~-X}Ggi!pIIF>uFx%aPARGQsnC8ZQc8lrQ5o~smqOg>Ti^GNme94*w z)JZy{_{#$jxGQ&`M z!OMvZMHR>8*^>eS%o*6hJwn!l8VOOjZQJvh)@tnHVW&*GYPuxqXw}%M!(f-SQf`=L z5;=5w2;%82VMH6Xi&-K3W)o&K^+vJCepWZ-rW%+Dc6X3(){z$@4zjYxQ|}8UIojeC zYZpQ1dU{fy=oTr<4VX?$q)LP}IUmpiez^O&N3E_qPpchGTi5ZM6-2ScWlQq%V&R2Euz zO|Q0Hx>lY1Q1cW5xHv5!0OGU~PVEqSuy#fD72d#O`N!C;o=m+YioGu-wH2k6!t<~K zSr`E=W9)!g==~x9VV~-8{4ZN9{~-A9zJpRe%NGg$+MDuI-dH|b@BD)~>pPCGUNNzY zMDg||0@XGQgw`YCt5C&A{_+J}mvV9Wg{6V%2n#YSRN{AP#PY?1FF1#|vO_%e+#`|2*~wGAJaeRX6=IzFNeWhz6gJc8+(03Ph4y6ELAm=AkN7TOgMUEw*N{= z_)EIDQx5q22oUR+_b*tazu9+pX|n1c*IB-}{DqIj z-?E|ks{o3AGRNb;+iKcHkZvYJvFsW&83RAPs1Oh@IWy%l#5x2oUP6ZCtv+b|q>jsf zZ_9XO;V!>n`UxH1LvH8)L4?8raIvasEhkpQoJ`%!5rBs!0Tu(s_D{`4opB;57)pkX z4$A^8CsD3U5*!|bHIEqsn~{q+Ddj$ME@Gq4JXtgVz&7l{Ok!@?EA{B3P~NAqb9)4? zkQo30A^EbHfQ@87G5&EQTd`frrwL)&Yw?%-W@uy^Gn23%j?Y!Iea2xw<-f;esq zf%w5WN@E1}zyXtYv}}`U^B>W`>XPmdLj%4{P298|SisrE;7HvXX;A}Ffi8B#3Lr;1 zHt6zVb`8{#+e$*k?w8|O{Uh|&AG}|DG1PFo1i?Y*cQm$ZwtGcVgMwtBUDa{~L1KT-{jET4w60>{KZ27vXrHJ;fW{6| z=|Y4!&UX020wU1>1iRgB@Q#m~1^Z^9CG1LqDhYBrnx%IEdIty z!46iOoKlKs)c}newDG)rWUikD%j`)p z_w9Ph&e40=(2eBy;T!}*1p1f1SAUDP9iWy^u^Ubdj21Kn{46;GR+hwLO=4D11@c~V zI8x&(D({K~Df2E)Nx_yQvYfh4;MbMJ@Z}=Dt3_>iim~QZ*hZIlEs0mEb z_54+&*?wMD`2#vsQRN3KvoT>hWofI_Vf(^C1ff-Ike@h@saEf7g}<9T`W;HAne-Nd z>RR+&SP35w)xKn8^U$7))PsM!jKwYZ*RzEcG-OlTrX3}9a{q%#Un5E5W{{hp>w~;` zGky+3(vJvQyGwBo`tCpmo0mo((?nM8vf9aXrrY1Ve}~TuVkB(zeds^jEfI}xGBCM2 zL1|#tycSaWCurP+0MiActG3LCas@_@tao@(R1ANlwB$4K53egNE_;!&(%@Qo$>h`^1S_!hN6 z)vZtG$8fN!|BXBJ=SI>e(LAU(y(i*PHvgQ2llulxS8>qsimv7yL}0q_E5WiAz7)(f zC(ahFvG8&HN9+6^jGyLHM~$)7auppeWh_^zKk&C_MQ~8;N??OlyH~azgz5fe^>~7F zl3HnPN3z-kN)I$4@`CLCMQx3sG~V8hPS^}XDXZrQA>}mQPw%7&!sd(Pp^P=tgp-s^ zjl}1-KRPNWXgV_K^HkP__SR`S-|OF0bR-N5>I%ODj&1JUeAQ3$9i;B~$S6}*^tK?= z**%aCiH7y?xdY?{LgVP}S0HOh%0%LI$wRx;$T|~Y8R)Vdwa}kGWv8?SJVm^>r6+%I z#lj1aR94{@MP;t-scEYQWc#xFA30^}?|BeX*W#9OL;Q9#WqaaM546j5j29((^_8Nu z4uq}ESLr~r*O7E7$D{!k9W>`!SLoyA53i9QwRB{!pHe8um|aDE`Cg0O*{jmor)^t)3`>V>SWN-2VJcFmj^1?~tT=JrP`fVh*t zXHarp=8HEcR#vFe+1a%XXuK+)oFs`GDD}#Z+TJ}Ri`FvKO@ek2ayn}yaOi%(8p%2$ zpEu)v0Jym@f}U|-;}CbR=9{#<^z28PzkkTNvyKvJDZe+^VS2bES3N@Jq!-*}{oQlz z@8bgC_KnDnT4}d#&Cpr!%Yb?E!brx0!eVOw~;lLwUoz#Np%d$o%9scc3&zPm`%G((Le|6o1 zM(VhOw)!f84zG^)tZ1?Egv)d8cdNi+T${=5kV+j;Wf%2{3g@FHp^Gf*qO0q!u$=m9 zCaY`4mRqJ;FTH5`a$affE5dJrk~k`HTP_7nGTY@B9o9vvnbytaID;^b=Tzp7Q#DmD zC(XEN)Ktn39z5|G!wsVNnHi) z%^q94!lL|hF`IijA^9NR0F$@h7k5R^ljOW(;Td9grRN0Mb)l_l7##{2nPQ@?;VjXv zaLZG}yuf$r$<79rVPpXg?6iiieX|r#&`p#Con2i%S8*8F}(E) zI5E6c3tG*<;m~6>!&H!GJ6zEuhH7mkAzovdhLy;)q z{H2*8I^Pb}xC4s^6Y}6bJvMu=8>g&I)7!N!5QG$xseeU#CC?ZM-TbjsHwHgDGrsD= z{%f;@Sod+Ch66Ko2WF~;Ty)v>&x^aovCbCbD7>qF*!?BXmOV3(s|nxsb*Lx_2lpB7 zokUnzrk;P=T-&kUHO}td+Zdj!3n&NR?K~cRU zAXU!DCp?51{J4w^`cV#ye}(`SQhGQkkMu}O3M*BWt4UsC^jCFUy;wTINYmhD$AT;4 z?Xd{HaJjP`raZ39qAm;%beDbrLpbRf(mkKbANan7XsL>_pE2oo^$TgdidjRP!5-`% zv0d!|iKN$c0(T|L0C~XD0aS8t{*&#LnhE;1Kb<9&=c2B+9JeLvJr*AyyRh%@jHej=AetOMSlz^=!kxX>>B{2B1uIrQyfd8KjJ+DBy!h)~*(!|&L4^Q_07SQ~E zcemVP`{9CwFvPFu7pyVGCLhH?LhEVb2{7U+Z_>o25#+3<|8%1T^5dh}*4(kfJGry} zm%r#hU+__Z;;*4fMrX=Bkc@7|v^*B;HAl0((IBPPii%X9+u3DDF6%bI&6?Eu$8&aWVqHIM7mK6?Uvq$1|(-T|)IV<>e?!(rY zqkmO1MRaLeTR=)io(0GVtQT@s6rN%C6;nS3@eu;P#ry4q;^O@1ZKCJyp_Jo)Ty^QW z+vweTx_DLm{P-XSBj~Sl<%_b^$=}odJ!S2wAcxenmzFGX1t&Qp8Vxz2VT`uQsQYtdn&_0xVivIcxZ_hnrRtwq4cZSj1c-SG9 z7vHBCA=fd0O1<4*=lu$6pn~_pVKyL@ztw1swbZi0B?spLo56ZKu5;7ZeUml1Ws1?u zqMf1p{5myAzeX$lAi{jIUqo1g4!zWLMm9cfWcnw`k6*BR^?$2(&yW?>w;G$EmTA@a z6?y#K$C~ZT8+v{87n5Dm&H6Pb_EQ@V0IWmG9cG=O;(;5aMWWrIPzz4Q`mhK;qQp~a z+BbQrEQ+w{SeiuG-~Po5f=^EvlouB@_|4xQXH@A~KgpFHrwu%dwuCR)=B&C(y6J4J zvoGk9;lLs9%iA-IJGU#RgnZZR+@{5lYl8(e1h6&>Vc_mvg0d@);X zji4T|n#lB!>pfL|8tQYkw?U2bD`W{na&;*|znjmalA&f;*U++_aBYerq;&C8Kw7mI z7tsG*?7*5j&dU)Lje;^{D_h`%(dK|pB*A*1(Jj)w^mZ9HB|vGLkF1GEFhu&rH=r=8 zMxO42e{Si6$m+Zj`_mXb&w5Q(i|Yxyg?juUrY}78uo@~3v84|8dfgbPd0iQJRdMj< zncCNGdMEcsxu#o#B5+XD{tsg*;j-eF8`mp~K8O1J!Z0+>0=7O=4M}E?)H)ENE;P*F z$Ox?ril_^p0g7xhDUf(q652l|562VFlC8^r8?lQv;TMvn+*8I}&+hIQYh2 z1}uQQaag&!-+DZ@|C+C$bN6W;S-Z@)d1|en+XGvjbOxCa-qAF*LA=6s(Jg+g;82f$ z(Vb)8I)AH@cdjGFAR5Rqd0wiNCu!xtqWbcTx&5kslzTb^7A78~Xzw1($UV6S^VWiP zFd{Rimd-0CZC_Bu(WxBFW7+k{cOW7DxBBkJdJ;VsJ4Z@lERQr%3eVv&$%)b%<~ zCl^Y4NgO}js@u{|o~KTgH}>!* z_iDNqX2(As7T0xivMH|3SC1ivm8Q}6Ffcd7owUKN5lHAtzMM4<0v+ykUT!QiowO;`@%JGv+K$bBx@*S7C8GJVqQ_K>12}M`f_Ys=S zKFh}HM9#6Izb$Y{wYzItTy+l5U2oL%boCJn?R3?jP@n$zSIwlmyGq30Cw4QBO|14` zW5c);AN*J3&eMFAk$SR~2k|&+&Bc$e>s%c{`?d~85S-UWjA>DS5+;UKZ}5oVa5O(N zqqc@>)nee)+4MUjH?FGv%hm2{IlIF-QX}ym-7ok4Z9{V+ZHVZQl$A*x!(q%<2~iVv znUa+BX35&lCb#9VE-~Y^W_f;Xhl%vgjwdjzMy$FsSIj&ok}L+X`4>J=9BkN&nu^E*gbhj3(+D>C4E z@Fwq_=N)^bKFSHTzZk?-gNU$@l}r}dwGyh_fNi=9b|n}J>&;G!lzilbWF4B}BBq4f zYIOl?b)PSh#XTPp4IS5ZR_2C!E)Z`zH0OW%4;&~z7UAyA-X|sh9@~>cQW^COA9hV4 zXcA6qUo9P{bW1_2`eo6%hgbN%(G-F1xTvq!sc?4wN6Q4`e9Hku zFwvlAcRY?6h^Fj$R8zCNEDq8`=uZB8D-xn)tA<^bFFy}4$vA}Xq0jAsv1&5!h!yRA zU()KLJya5MQ`q&LKdH#fwq&(bNFS{sKlEh_{N%{XCGO+po#(+WCLmKW6&5iOHny>g z3*VFN?mx!16V5{zyuMWDVP8U*|BGT$(%IO|)?EF|OI*sq&RovH!N%=>i_c?K*A>>k zyg1+~++zY4Q)J;VWN0axhoIKx;l&G$gvj(#go^pZskEVj8^}is3Jw26LzYYVos0HX zRPvmK$dVxM8(Tc?pHFe0Z3uq){{#OK3i-ra#@+;*=ui8)y6hsRv z4Fxx1c1+fr!VI{L3DFMwXKrfl#Q8hfP@ajgEau&QMCxd{g#!T^;ATXW)nUg&$-n25 zruy3V!!;{?OTobo|0GAxe`Acn3GV@W=&n;~&9 zQM>NWW~R@OYORkJAo+eq1!4vzmf9K%plR4(tB@TR&FSbDoRgJ8qVcH#;7lQub*nq&?Z>7WM=oeEVjkaG zT#f)=o!M2DO5hLR+op>t0CixJCIeXH*+z{-XS|%jx)y(j&}Wo|3!l7{o)HU3m7LYyhv*xF&tq z%IN7N;D4raue&&hm0xM=`qv`+TK@;_xAcGKuK(2|75~ar2Yw)geNLSmVxV@x89bQu zpViVKKnlkwjS&&c|-X6`~xdnh}Ps)Hs z4VbUL^{XNLf7_|Oi>tA%?SG5zax}esF*FH3d(JH^Gvr7Rp*n=t7frH!U;!y1gJB^i zY_M$KL_}mW&XKaDEi9K-wZR|q*L32&m+2n_8lq$xRznJ7p8}V>w+d@?uB!eS3#u<} zIaqi!b!w}a2;_BfUUhGMy#4dPx>)_>yZ`ai?Rk`}d0>~ce-PfY-b?Csd(28yX22L% zI7XI>OjIHYTk_@Xk;Gu^F52^Gn6E1&+?4MxDS2G_#PQ&yXPXP^<-p|2nLTb@AAQEY zI*UQ9Pmm{Kat}wuazpjSyXCdnrD&|C1c5DIb1TnzF}f4KIV6D)CJ!?&l&{T)e4U%3HTSYqsQ zo@zWB1o}ceQSV)<4G<)jM|@@YpL+XHuWsr5AYh^Q{K=wSV99D~4RRU52FufmMBMmd z_H}L#qe(}|I9ZyPRD6kT>Ivj&2Y?qVZq<4bG_co_DP`sE*_Xw8D;+7QR$Uq(rr+u> z8bHUWbV19i#)@@G4bCco@Xb<8u~wVDz9S`#k@ciJtlu@uP1U0X?yov8v9U3VOig2t zL9?n$P3=1U_Emi$#slR>N5wH-=J&T=EdUHA}_Z zZIl3nvMP*AZS9{cDqFanrA~S5BqxtNm9tlu;^`)3X&V4tMAkJ4gEIPl= zoV!Gyx0N{3DpD@)pv^iS*dl2FwANu;1;%EDl}JQ7MbxLMAp>)UwNwe{=V}O-5C*>F zu?Ny+F64jZn<+fKjF01}8h5H_3pey|;%bI;SFg$w8;IC<8l|3#Lz2;mNNik6sVTG3 z+Su^rIE#40C4a-587$U~%KedEEw1%r6wdvoMwpmlXH$xPnNQN#f%Z7|p)nC>WsuO= z4zyqapLS<8(UJ~Qi9d|dQijb_xhA2)v>la)<1md5s^R1N&PiuA$^k|A<+2C?OiHbj z>Bn$~t)>Y(Zb`8hW7q9xQ=s>Rv81V+UiuZJc<23HplI88isqRCId89fb`Kt|CxVIg znWcwprwXnotO>3s&Oypkte^9yJjlUVVxSe%_xlzmje|mYOVPH^vjA=?6xd0vaj0Oz zwJ4OJNiFdnHJX3rw&inskjryukl`*fRQ#SMod5J|KroJRsVXa5_$q7whSQ{gOi*s0 z1LeCy|JBWRsDPn7jCb4s(p|JZiZ8+*ExC@Vj)MF|*Vp{B(ziccSn`G1Br9bV(v!C2 z6#?eqpJBc9o@lJ#^p-`-=`4i&wFe>2)nlPK1p9yPFzJCzBQbpkcR>={YtamIw)3nt z(QEF;+)4`>8^_LU)_Q3 zC5_7lgi_6y>U%m)m@}Ku4C}=l^J=<<7c;99ec3p{aR+v=diuJR7uZi%aQv$oP?dn?@6Yu_+*^>T0ptf(oobdL;6)N-I!TO`zg^Xbv3#L0I~sn@WGk-^SmPh5>W+LB<+1PU}AKa?FCWF|qMNELOgdxR{ zbqE7@jVe+FklzdcD$!(A$&}}H*HQFTJ+AOrJYnhh}Yvta(B zQ_bW4Rr;R~&6PAKwgLWXS{Bnln(vUI+~g#kl{r+_zbngT`Y3`^Qf=!PxN4IYX#iW4 zucW7@LLJA9Zh3(rj~&SyN_pjO8H&)|(v%!BnMWySBJV=eSkB3YSTCyIeJ{i;(oc%_hk{$_l;v>nWSB)oVeg+blh=HB5JSlG_r7@P z3q;aFoZjD_qS@zygYqCn=;Zxjo!?NK!%J$ z52lOP`8G3feEj+HTp@Tnn9X~nG=;tS+z}u{mQX_J0kxtr)O30YD%oo)L@wy`jpQYM z@M>Me=95k1p*FW~rHiV1CIfVc{K8r|#Kt(ApkXKsDG$_>76UGNhHExFCw#Ky9*B-z zNq2ga*xax!HMf_|Vp-86r{;~YgQKqu7%szk8$hpvi_2I`OVbG1doP(`gn}=W<8%Gn z%81#&WjkH4GV;4u43EtSW>K_Ta3Zj!XF?;SO3V#q=<=>Tc^@?A`i;&`-cYj|;^ zEo#Jl5zSr~_V-4}y8pnufXLa80vZY4z2ko7fj>DR)#z=wWuS1$$W!L?(y}YC+yQ|G z@L&`2upy3f>~*IquAjkVNU>}c10(fq#HdbK$~Q3l6|=@-eBbo>B9(6xV`*)sae58*f zym~RRVx;xoCG3`JV`xo z!lFw)=t2Hy)e!IFs?0~7osWk(d%^wxq&>_XD4+U#y&-VF%4z?XH^i4w`TxpF{`XhZ z%G}iEzf!T(l>g;W9<~K+)$g!{UvhW{E0Lis(S^%I8OF&%kr!gJ&fMOpM=&=Aj@wuL zBX?*6i51Qb$uhkwkFYkaD_UDE+)rh1c;(&Y=B$3)J&iJfQSx!1NGgPtK!$c9OtJuu zX(pV$bfuJpRR|K(dp@^j}i&HeJOh@|7lWo8^$*o~Xqo z5Sb+!EtJ&e@6F+h&+_1ETbg7LfP5GZjvIUIN3ibCOldAv z)>YdO|NH$x7AC8dr=<2ekiY1%fN*r~e5h6Yaw<{XIErujKV~tiyrvV_DV0AzEknC- zR^xKM3i<1UkvqBj3C{wDvytOd+YtDSGu!gEMg+!&|8BQrT*|p)(dwQLEy+ zMtMzij3zo40)CA!BKZF~yWg?#lWhqD3@qR)gh~D{uZaJO;{OWV8XZ_)J@r3=)T|kt zUS1pXr6-`!Z}w2QR7nP%d?ecf90;K_7C3d!UZ`N(TZoWNN^Q~RjVhQG{Y<%E1PpV^4 z-m-K+$A~-+VDABs^Q@U*)YvhY4Znn2^w>732H?NRK(5QSS$V@D7yz2BVX4)f5A04~$WbxGOam22>t&uD)JB8-~yiQW6ik;FGblY_I>SvB_z2?PS z*Qm&qbKI{H1V@YGWzpx`!v)WeLT02};JJo*#f$a*FH?IIad-^(;9XC#YTWN6;Z6+S zm4O1KH=#V@FJw7Pha0!9Vb%ZIM$)a`VRMoiN&C|$YA3~ZC*8ayZRY^fyuP6$n%2IU z$#XceYZeqLTXw(m$_z|33I$B4k~NZO>pP6)H_}R{E$i%USGy{l{-jOE;%CloYPEU+ zRFxOn4;7lIOh!7abb23YKD+_-?O z0FP9otcAh+oSj;=f#$&*ExUHpd&e#bSF%#8*&ItcL2H$Sa)?pt0Xtf+t)z$_u^wZi z44oE}r4kIZGy3!Mc8q$B&6JqtnHZ>Znn!Zh@6rgIu|yU+zG8q`q9%B18|T|oN3zMq z`l&D;U!OL~%>vo&q0>Y==~zLiCZk4v%s_7!9DxQ~id1LLE93gf*gg&2$|hB#j8;?3 z5v4S;oM6rT{Y;I+#FdmNw z){d%tNM<<#GN%n9ox7B=3#;u7unZ~tLB_vRZ52a&2=IM)2VkXm=L+Iqq~uk#Dug|x z>S84e+A7EiOY5lj*!q?6HDkNh~0g;0Jy(al!ZHHDtur9T$y-~)94HelX1NHjXWIM7UAe}$?jiz z9?P4`I0JM=G5K{3_%2jPLC^_Mlw?-kYYgb7`qGa3@dn|^1fRMwiyM@Ch z;CB&o7&&?c5e>h`IM;Wnha0QKnEp=$hA8TJgR-07N~U5(>9vJzeoFsSRBkDq=x(YgEMpb=l4TDD`2 zwVJpWGTA_u7}?ecW7s6%rUs&NXD3+n;jB86`X?8(l3MBo6)PdakI6V6a}22{)8ilT zM~T*mU}__xSy|6XSrJ^%lDAR3Lft%+yxC|ZUvSO_nqMX!_ul3;R#*{~4DA=h$bP)%8Yv9X zyp><|e8=_ttI}ZAwOd#dlnSjck#6%273{E$kJuCGu=I@O)&6ID{nWF5@gLb16sj|&Sb~+du4e4O_%_o`Ix4NRrAsyr1_}MuP94s>de8cH-OUkVPk3+K z&jW)It9QiU-ti~AuJkL`XMca8Oh4$SyJ=`-5WU<{cIh+XVH#e4d&zive_UHC!pN>W z3TB;Mn5i)9Qn)#6@lo4QpI3jFYc0~+jS)4AFz8fVC;lD^+idw^S~Qhq>Tg(!3$yLD zzktzoFrU@6s4wwCMz}edpF5i5Q1IMmEJQHzp(LAt)pgN3&O!&d?3W@6U4)I^2V{;- z6A(?zd93hS*uQmnh4T)nHnE{wVhh(=MMD(h(P4+^p83Om6t<*cUW>l(qJzr%5vp@K zN27ka(L{JX=1~e2^)F^i=TYj&;<7jyUUR2Bek^A8+3Up*&Xwc{)1nRR5CT8vG>ExV zHnF3UqXJOAno_?bnhCX-&kwI~Ti8t4`n0%Up>!U`ZvK^w2+0Cs-b9%w%4`$+To|k= zKtgc&l}P`*8IS>8DOe?EB84^kx4BQp3<7P{Pq}&p%xF_81pg!l2|u=&I{AuUgmF5n zJQCTLv}%}xbFGYtKfbba{CBo)lWW%Z>i(_NvLhoQZ*5-@2l&x>e+I~0Nld3UI9tdL zRzu8}i;X!h8LHVvN?C+|M81e>Jr38%&*9LYQec9Ax>?NN+9(_>XSRv&6hlCYB`>Qm z1&ygi{Y()OU4@D_jd_-7vDILR{>o|7-k)Sjdxkjgvi{@S>6GqiF|o`*Otr;P)kLHN zZkpts;0zw_6;?f(@4S1FN=m!4^mv~W+lJA`&7RH%2$)49z0A+8@0BCHtj|yH--AEL z0tW6G%X-+J+5a{5*WKaM0QDznf;V?L5&uQw+yegDNDP`hA;0XPYc6e0;Xv6|i|^F2WB)Z$LR|HR4 zTQsRAby9(^Z@yATyOgcfQw7cKyr^3Tz7lc7+JEwwzA7)|2x+PtEb>nD(tpxJQm)Kn zW9K_*r!L%~N*vS8<5T=iv|o!zTe9k_2jC_j*7ik^M_ zaf%k{WX{-;0*`t`G!&`eW;gChVXnJ-Rn)To8vW-?>>a%QU1v`ZC=U)f8iA@%JG0mZ zDqH;~mgBnrCP~1II<=V9;EBL)J+xzCoiRBaeH&J6rL!{4zIY8tZka?_FBeQeNO3q6 zyG_alW54Ba&wQf{&F1v-r1R6ID)PTsqjIBc+5MHkcW5Fnvi~{-FjKe)t1bl}Y;z@< z=!%zvpRua>>t_x}^}z0<7MI!H2v6|XAyR9!t50q-A)xk0nflgF4*OQlCGK==4S|wc zRMsSscNhRzHMBU8TdcHN!q^I}x0iXJ%uehac|Zs_B$p@CnF)HeXPpB_Za}F{<@6-4 zl%kml@}kHQ(ypD8FsPJ2=14xXJE|b20RUIgs!2|R3>LUMGF6X*B_I|$`Qg=;zm7C z{mEDy9dTmPbued7mlO@phdmAmJ7p@GR1bjCkMw6*G7#4+`k>fk1czdJUB!e@Q(~6# zwo%@p@V5RL0ABU2LH7Asq^quDUho@H>eTZH9f*no9fY0T zD_-9px3e}A!>>kv5wk91%C9R1J_Nh!*&Kk$J3KNxC}c_@zlgpJZ+5L)Nw|^p=2ue}CJtm;uj*Iqr)K})kA$xtNUEvX;4!Px*^&9T_`IN{D z{6~QY=Nau6EzpvufB^hflc#XIsSq0Y9(nf$d~6ZwK}fal92)fr%T3=q{0mP-EyP_G z)UR5h@IX}3Qll2b0oCAcBF>b*@Etu*aTLPU<%C>KoOrk=x?pN!#f_Og-w+;xbFgjQ zXp`et%lDBBh~OcFnMKMUoox0YwBNy`N0q~bSPh@+enQ=4RUw1) zpovN`QoV>vZ#5LvC;cl|6jPr}O5tu!Ipoyib8iXqy}TeJ;4+_7r<1kV0v5?Kv>fYp zg>9L`;XwXa&W7-jf|9~uP2iyF5`5AJ`Q~p4eBU$MCC00`rcSF>`&0fbd^_eqR+}mK z4n*PMMa&FOcc)vTUR zlDUAn-mh`ahi_`f`=39JYTNVjsTa_Y3b1GOIi)6dY)D}xeshB0T8Eov5%UhWd1)u}kjEQ|LDo{tqKKrYIfVz~@dp!! zMOnah@vp)%_-jDTUG09l+;{CkDCH|Q{NqX*uHa1YxFShy*1+;J`gywKaz|2Q{lG8x zP?KBur`}r`!WLKXY_K;C8$EWG>jY3UIh{+BLv0=2)KH%P}6xE2kg)%(-uA6lC?u8}{K(#P*c zE9C8t*u%j2r_{;Rpe1A{9nNXU;b_N0vNgyK!EZVut~}+R2rcbsHilqsOviYh-pYX= zHw@53nlmwYI5W5KP>&`dBZe0Jn?nAdC^HY1wlR6$u^PbpB#AS&5L6zqrXN&7*N2Q` z+Rae1EwS)H=aVSIkr8Ek^1jy2iS2o7mqm~Mr&g5=jjt7VxwglQ^`h#Mx+x2v|9ZAwE$i_9918MjJxTMr?n!bZ6n$}y11u8I9COTU`Z$Fi z!AeAQLMw^gp_{+0QTEJrhL424pVDp%wpku~XRlD3iv{vQ!lAf!_jyqd_h}+Tr1XG| z`*FT*NbPqvHCUsYAkFnM`@l4u_QH&bszpUK#M~XLJt{%?00GXY?u_{gj3Hvs!=N(I z(=AuWPijyoU!r?aFTsa8pLB&cx}$*%;K$e*XqF{~*rA-qn)h^!(-;e}O#B$|S~c+U zN4vyOK0vmtx$5K!?g*+J@G1NmlEI=pyZXZ69tAv=@`t%ag_Hk{LP~OH9iE)I= zaJ69b4kuCkV0V zo(M0#>phpQ_)@j;h%m{-a*LGi(72TP)ws2w*@4|C-3+;=5DmC4s7Lp95%n%@Ko zfdr3-a7m*dys9iIci$A=4NPJ`HfJ;hujLgU)ZRuJI`n;Pw|yksu!#LQnJ#dJysgNb z@@qwR^wrk(jbq4H?d!lNyy72~Dnn87KxsgQ!)|*m(DRM+eC$wh7KnS-mho3|KE)7h zK3k;qZ;K1Lj6uEXLYUYi)1FN}F@-xJ z@@3Hb84sl|j{4$3J}aTY@cbX@pzB_qM~APljrjju6P0tY{C@ zpUCOz_NFmALMv1*blCcwUD3?U6tYs+N%cmJ98D%3)%)Xu^uvzF zS5O!sc#X6?EwsYkvPo6A%O8&y8sCCQH<%f2togVwW&{M;PR!a(ZT_A+jVAbf{@5kL zB@Z(hb$3U{T_}SKA_CoQVU-;j>2J=L#lZ~aQCFg-d<9rzs$_gO&d5N6eFSc z1ml8)P*FSi+k@!^M9nDWR5e@ATD8oxtDu=36Iv2!;dZzidIS(PCtEuXAtlBb1;H%Z zwnC^Ek*D)EX4#Q>R$$WA2sxC_t(!!6Tr?C#@{3}n{<^o;9id1RA&-Pig1e-2B1XpG zliNjgmd3c&%A}s>qf{_j#!Z`fu0xIwm4L0)OF=u(OEmp;bLCIaZX$&J_^Z%4Sq4GZ zPn6sV_#+6pJmDN_lx@1;Zw6Md_p0w9h6mHtzpuIEwNn>OnuRSC2=>fP^Hqgc)xu^4 z<3!s`cORHJh#?!nKI`Et7{3C27+EuH)Gw1f)aoP|B3y?fuVfvpYYmmukx0ya-)TQX zR{ggy5cNf4X|g)nl#jC9p>7|09_S7>1D2GTRBUTW zAkQ=JMRogZqG#v;^=11O6@rPPwvJkr{bW-Qg8`q8GoD#K`&Y+S#%&B>SGRL>;ZunM@49!}Uy zN|bBCJ%sO;@3wl0>0gbl3L@1^O60ONObz8ZI7nder>(udj-jt`;yj^nTQ$L9`OU9W zX4alF#$|GiR47%x@s&LV>2Sz2R6?;2R~5k6V>)nz!o_*1Y!$p>BC5&?hJg_MiE6UBy>RkVZj`9UWbRkN-Hk!S`=BS3t3uyX6)7SF#)71*}`~Ogz z1rap5H6~dhBJ83;q-Y<5V35C2&F^JI-it(=5D#v!fAi9p#UwV~2tZQI+W(Dv?1t9? zfh*xpxxO{-(VGB>!Q&0%^YW_F!@aZS#ucP|YaD#>wd1Fv&Z*SR&mc;asi}1G) z_H>`!akh-Zxq9#io(7%;a$)w+{QH)Y$?UK1Dt^4)up!Szcxnu}kn$0afcfJL#IL+S z5gF_Y30j;{lNrG6m~$Ay?)*V9fZuU@3=kd40=LhazjFrau>(Y>SJNtOz>8x_X-BlA zIpl{i>OarVGj1v(4?^1`R}aQB&WCRQzS~;7R{tDZG=HhgrW@B`W|#cdyj%YBky)P= zpxuOZkW>S6%q7U{VsB#G(^FMsH5QuGXhb(sY+!-R8Bmv6Sx3WzSW<1MPPN1!&PurYky(@`bP9tz z52}LH9Q?+FF5jR6-;|+GVdRA!qtd;}*-h&iIw3Tq3qF9sDIb1FFxGbo&fbG5n8$3F zyY&PWL{ys^dTO}oZ#@sIX^BKW*bon=;te9j5k+T%wJ zNJtoN1~YVj4~YRrlZl)b&kJqp+Z`DqT!la$x&&IxgOQw#yZd-nBP3!7FijBXD|IsU8Zl^ zc6?MKpJQ+7ka|tZQLfchD$PD|;K(9FiLE|eUZX#EZxhG!S-63C$jWX1Yd!6-Yxi-u zjULIr|0-Q%D9jz}IF~S%>0(jOqZ(Ln<$9PxiySr&2Oic7vb<8q=46)Ln%Z|<*z5&> z3f~Zw@m;vR(bESB<=Jqkxn(=#hQw42l(7)h`vMQQTttz9XW6^|^8EK7qhju4r_c*b zJIi`)MB$w@9epwdIfnEBR+?~);yd6C(LeMC& zn&&N*?-g&BBJcV;8&UoZi4Lmxcj16ojlxR~zMrf=O_^i1wGb9X-0@6_rpjPYemIin zmJb+;lHe;Yp=8G)Q(L1bzH*}I>}uAqhj4;g)PlvD9_e_ScR{Ipq|$8NvAvLD8MYr}xl=bU~)f%B3E>r3Bu9_t|ThF3C5~BdOve zEbk^r&r#PT&?^V1cb{72yEWH}TXEE}w>t!cY~rA+hNOTK8FAtIEoszp!qqptS&;r$ zaYV-NX96-h$6aR@1xz6_E0^N49mU)-v#bwtGJm)ibygzJ8!7|WIrcb`$XH~^!a#s& z{Db-0IOTFq#9!^j!n_F}#Z_nX{YzBK8XLPVmc&X`fT7!@$U-@2KM9soGbmOSAmqV z{nr$L^MBo_u^Joyf0E^=eo{Rt0{{e$IFA(#*kP@SQd6lWT2-#>` zP1)7_@IO!9lk>Zt?#CU?cuhiLF&)+XEM9B)cS(gvQT!X3`wL*{fArTS;Ak`J<84du zALKPz4}3nlG8Fo^MH0L|oK2-4xIY!~Oux~1sw!+It)&D3p;+N8AgqKI`ld6v71wy8I!eP0o~=RVcFQR2Gr(eP_JbSytoQ$Yt}l*4r@A8Me94y z8cTDWhqlq^qoAhbOzGBXv^Wa4vUz$(7B!mX`T=x_ueKRRDfg&Uc-e1+z4x$jyW_Pm zp?U;-R#xt^Z8Ev~`m`iL4*c#65Nn)q#=Y0l1AuD&+{|8-Gsij3LUZXpM0Bx0u7WWm zH|%yE@-#XEph2}-$-thl+S;__ciBxSSzHveP%~v}5I%u!z_l_KoW{KRx2=eB33umE zIYFtu^5=wGU`Jab8#}cnYry@9p5UE#U|VVvx_4l49JQ;jQdp(uw=$^A$EA$LM%vmE zvdEOaIcp5qX8wX{mYf0;#51~imYYPn4=k&#DsKTxo{_Mg*;S495?OBY?#gv=edYC* z^O@-sd-qa+U24xvcbL0@C7_6o!$`)sVr-jSJE4XQUQ$?L7}2(}Eixqv;L8AdJAVqc zq}RPgpnDb@E_;?6K58r3h4-!4rT4Ab#rLHLX?eMOfluJk=3i1@Gt1i#iA=O`M0@x! z(HtJP9BMHXEzuD93m|B&woj0g6T?f#^)>J>|I4C5?Gam>n9!8CT%~aT;=oco5d6U8 zMXl(=W;$ND_8+DD*?|5bJ!;8ebESXMUKBAf7YBwNVJibGaJ*(2G`F%wx)grqVPjudiaq^Kl&g$8A2 zWMxMr@_$c}d+;_B`#kUX-t|4VKH&_f^^EP0&=DPLW)H)UzBG%%Tra*5 z%$kyZe3I&S#gfie^z5)!twG={3Cuh)FdeA!Kj<-9** zvT*5%Tb`|QbE!iW-XcOuy39>D3oe6x{>&<#E$o8Ac|j)wq#kQzz|ATd=Z0K!p2$QE zPu?jL8Lb^y3_CQE{*}sTDe!2!dtlFjq&YLY@2#4>XS`}v#PLrpvc4*@q^O{mmnr5D zmyJq~t?8>FWU5vZdE(%4cuZuao0GNjp3~Dt*SLaxI#g_u>hu@k&9Ho*#CZP~lFJHj z(e!SYlLigyc?&5-YxlE{uuk$9b&l6d`uIlpg_z15dPo*iU&|Khx2*A5Fp;8iK_bdP z?T6|^7@lcx2j0T@x>X7|kuuBSB7<^zeY~R~4McconTxA2flHC0_jFxmSTv-~?zVT| zG_|yDqa9lkF*B6_{j=T>=M8r<0s;@z#h)3BQ4NLl@`Xr__o7;~M&dL3J8fP&zLfDfy z);ckcTev{@OUlZ`bCo(-3? z1u1xD`PKgSg?RqeVVsF<1SLF;XYA@Bsa&cY!I48ZJn1V<3d!?s=St?TLo zC0cNr`qD*M#s6f~X>SCNVkva^9A2ZP>CoJ9bvgXe_c}WdX-)pHM5m7O zrHt#g$F0AO+nGA;7dSJ?)|Mo~cf{z2L)Rz!`fpi73Zv)H=a5K)*$5sf_IZypi($P5 zsPwUc4~P-J1@^3C6-r9{V-u0Z&Sl7vNfmuMY4yy*cL>_)BmQF!8Om9Dej%cHxbIzA zhtV0d{=%cr?;bpBPjt@4w=#<>k5ee=TiWAXM2~tUGfm z$s&!Dm0R^V$}fOR*B^kGaipi~rx~A2cS0;t&khV1a4u38*XRUP~f za!rZMtay8bsLt6yFYl@>-y^31(*P!L^^s@mslZy(SMsv9bVoX`O#yBgEcjCmGpyc* zeH$Dw6vB5P*;jor+JOX@;6K#+xc)Z9B8M=x2a@Wx-{snPGpRmOC$zpsqW*JCh@M2Y z#K+M(>=#d^>Of9C`))h<=Bsy)6zaMJ&x-t%&+UcpLjV`jo4R2025 zXaG8EA!0lQa)|dx-@{O)qP6`$rhCkoQqZ`^SW8g-kOwrwsK8 z3ms*AIcyj}-1x&A&vSq{r=QMyp3CHdWH35!sad#!Sm>^|-|afB+Q;|Iq@LFgqIp#Z zD1%H+3I?6RGnk&IFo|u+E0dCxXz4yI^1i!QTu7uvIEH>i3rR{srcST`LIRwdV1P;W z+%AN1NIf@xxvVLiSX`8ILA8MzNqE&7>%jMzGt9wm78bo9<;h*W84i29^w!>V>{N+S zd`5Zmz^G;f=icvoOZfK5#1ctx*~UwD=ab4DGQXehQ!XYnak*dee%YN$_ZPL%KZuz$ zD;$PpT;HM^$KwtQm@7uvT`i6>Hae1CoRVM2)NL<2-k2PiX=eAx+-6j#JI?M}(tuBW zkF%jjLR)O`gI2fcPBxF^HeI|DWwQWHVR!;;{BXXHskxh8F@BMDn`oEi-NHt;CLymW z=KSv5)3dyzec0T5B*`g-MQ<;gz=nIWKUi9ko<|4I(-E0k$QncH>E4l z**1w&#={&zv4Tvhgz#c29`m|;lU-jmaXFMC11 z*dlXDMEOG>VoLMc>!rApwOu2prKSi*!w%`yzGmS+k(zm*CsLK*wv{S_0WX^8A-rKy zbk^Gf_92^7iB_uUF)EE+ET4d|X|>d&mdN?x@vxKAQk`O+r4Qdu>XGy(a(19g;=jU} zFX{O*_NG>!$@jh!U369Lnc+D~qch3uT+_Amyi}*k#LAAwh}k8IPK5a-WZ81ufD>l> z$4cF}GSz>ce`3FAic}6W4Z7m9KGO?(eWqi@L|5Hq0@L|&2flN1PVl}XgQ2q*_n2s3 zt5KtowNkTYB5b;SVuoXA@i5irXO)A&%7?V`1@HGCB&)Wgk+l|^XXChq;u(nyPB}b3 zY>m5jkxpZgi)zfbgv&ec4Zqdvm+D<?Im*mXweS9H+V>)zF#Zp3)bhl$PbISY{5=_z!8&*Jv~NYtI-g!>fDs zmvL5O^U%!^VaKA9gvKw|5?-jk>~%CVGvctKmP$kpnpfN{D8@X*Aazi$txfa%vd-|E z>kYmV66W!lNekJPom29LdZ%(I+ZLZYTXzTg*to~m?7vp%{V<~>H+2}PQ?PPAq`36R z<%wR8v6UkS>Wt#hzGk#44W<%9S=nBfB);6clKwnxY}T*w21Qc3_?IJ@4gYzC7s;WP zVQNI(M=S=JT#xsZy7G`cR(BP9*je0bfeN8JN5~zY(DDs0t{LpHOIbN);?T-69Pf3R zSNe*&p2%AwXHL>__g+xd4Hlc_vu<25H?(`nafS%)3UPP7_4;gk-9ckt8SJRTv5v0M z_Hww`qPudL?ajIR&X*;$y-`<)6dxx1U~5eGS13CB!lX;3w7n&lDDiArbAhSycd}+b zya_3p@A`$kQy;|NJZ~s44Hqo7Hwt}X86NK=(ey>lgWTtGL6k@Gy;PbO!M%1~Wcn2k zUFP|*5d>t-X*RU8g%>|(wwj*~#l4z^Aatf^DWd1Wj#Q*AY0D^V@sC`M zjJc6qXu0I7Y*2;;gGu!plAFzG=J;1%eIOdn zQA>J&e05UN*7I5@yRhK|lbBSfJ+5Uq;!&HV@xfPZrgD}kE*1DSq^=%{o%|LChhl#0 zlMb<^a6ixzpd{kNZr|3jTGeEzuo}-eLT-)Q$#b{!vKx8Tg}swCni>{#%vDY$Ww$84 zew3c9BBovqb}_&BRo#^!G(1Eg((BScRZ}C)Oz?y`T5wOrv);)b^4XR8 zhJo7+<^7)qB>I;46!GySzdneZ>n_E1oWZY;kf94#)s)kWjuJN1c+wbVoNQcmnv}{> zN0pF+Sl3E}UQ$}slSZeLJrwT>Sr}#V(dVaezCQl2|4LN`7L7v&siYR|r7M(*JYfR$ zst3=YaDw$FSc{g}KHO&QiKxuhEzF{f%RJLKe3p*7=oo`WNP)M(9X1zIQPP0XHhY3c znrP{$4#Ol$A0s|4S7Gx2L23dv*Gv2o;h((XVn+9+$qvm}s%zi6nI-_s6?mG! zj{DV;qesJb&owKeEK?=J>UcAlYckA7Sl+I&IN=yasrZOkejir*kE@SN`fk<8Fgx*$ zy&fE6?}G)d_N`){P~U@1jRVA|2*69)KSe_}!~?+`Yb{Y=O~_+@!j<&oVQQMnhoIRU zA0CyF1OFfkK44n*JD~!2!SCPM;PRSk%1XL=0&rz00wxPs&-_eapJy#$h!eqY%nS0{ z!aGg58JIJPF3_ci%n)QSVpa2H`vIe$RD43;#IRfDV&Ibit z+?>HW4{2wOfC6Fw)}4x}i1maDxcE1qi@BS*qcxD2gE@h3#4cgU*D-&3z7D|tVZWt= z-Cy2+*Cm@P4GN_TPUtaVyVesbVDazF@)j8VJ4>XZv!f%}&eO1SvIgr}4`A*3#vat< z_MoByL(qW6L7SFZ#|Gc1fFN)L2PxY+{B8tJp+pxRyz*87)vXR}*=&ahXjBlQKguuf zX6x<<6fQulE^C*KH8~W%ptpaC0l?b=_{~*U4?5Vt;dgM4t_{&UZ1C2j?b>b+5}{IF_CUyvz-@QZPMlJ)r_tS$9kH%RPv#2_nMb zRLj5;chJ72*U`Z@Dqt4$@_+k$%|8m(HqLG!qT4P^DdfvGf&){gKnGCX#H0!;W=AGP zbA&Z`-__a)VTS}kKFjWGk z%|>yE?t*EJ!qeQ%dPk$;xIQ+P0;()PCBDgjJm6Buj{f^awNoVx+9<|lg3%-$G(*f) zll6oOkN|yamn1uyl2*N-lnqRI1cvs_JxLTeahEK=THV$Sz*gQhKNb*p0fNoda#-&F zB-qJgW^g}!TtM|0bS2QZekW7_tKu%GcJ!4?lObt0z_$mZ4rbQ0o=^curCs3bJK6sq z9fu-aW-l#>z~ca(B;4yv;2RZ?tGYAU)^)Kz{L|4oPj zdOf_?de|#yS)p2v8-N||+XL=O*%3+y)oI(HbM)Ds?q8~HPzIP(vs*G`iddbWq}! z(2!VjP&{Z1w+%eUq^ '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/android/gradlew.bat b/android/gradlew.bat new file mode 100644 index 00000000..f127cfd4 --- /dev/null +++ b/android/gradlew.bat @@ -0,0 +1,91 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/android/spotless.gradle b/android/spotless.gradle new file mode 100644 index 00000000..b33852dc --- /dev/null +++ b/android/spotless.gradle @@ -0,0 +1,9 @@ +// formatter & linter configuration for java +apply plugin: 'com.diffplug.spotless' + +spotless { + java { + target 'src/fabric/**/*.java', 'src/main/java/**/*.java', 'src/paper/java/com/horcrux/svg/**/*.java' + googleJavaFormat() + } +} \ No newline at end of file diff --git a/android/src/fabric/java/com/horcrux/svg/FabricEnabledViewGroup.java b/android/src/fabric/java/com/horcrux/svg/FabricEnabledViewGroup.java index b30c62f9..81a78e70 100644 --- a/android/src/fabric/java/com/horcrux/svg/FabricEnabledViewGroup.java +++ b/android/src/fabric/java/com/horcrux/svg/FabricEnabledViewGroup.java @@ -1,59 +1,62 @@ package com.horcrux.svg; import android.content.Context; -import android.view.ViewGroup; import androidx.annotation.UiThread; import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.WritableMap; import com.facebook.react.bridge.WritableNativeMap; import com.facebook.react.uimanager.FabricViewStateManager; -import com.facebook.react.uimanager.PixelUtil; import com.facebook.react.uimanager.FabricViewStateManager.HasFabricViewStateManager; import com.facebook.react.uimanager.FabricViewStateManager.StateUpdateCallback; +import com.facebook.react.uimanager.PixelUtil; import com.facebook.react.views.view.ReactViewGroup; - import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public abstract class FabricEnabledViewGroup extends ReactViewGroup implements HasFabricViewStateManager { - private final FabricViewStateManager mFabricViewStateManager = new FabricViewStateManager(); +public abstract class FabricEnabledViewGroup extends ReactViewGroup + implements HasFabricViewStateManager { + private final FabricViewStateManager mFabricViewStateManager = new FabricViewStateManager(); - @NotNull - public FabricViewStateManager getFabricViewStateManager() { - return this.mFabricViewStateManager; - } + @NotNull + public FabricViewStateManager getFabricViewStateManager() { + return this.mFabricViewStateManager; + } - protected final void updateScreenSizeFabric(int width, int height) { - this.updateState(width, height); - } + protected final void updateScreenSizeFabric(int width, int height) { + this.updateState(width, height); + } - @UiThread - public final void updateState(int width, int height) { - final float realWidth = PixelUtil.toDIPFromPixel((float)width); - final float realHeight = PixelUtil.toDIPFromPixel((float)height); - ReadableMap currentState = this.mFabricViewStateManager.getStateData(); - if (currentState != null) { - float delta = 0.9F; - float stateFrameHeight = currentState.hasKey("frameHeight") ? (float)currentState.getDouble("frameHeight") : 0.0F; - float stateFrameWidth = currentState.hasKey("frameWidth") ? (float)currentState.getDouble("frameWidth") : 0.0F; - if (Math.abs(stateFrameWidth - realWidth) < delta && - Math.abs(stateFrameHeight - realHeight) < delta) { - return; - } + @UiThread + public final void updateState(int width, int height) { + final float realWidth = PixelUtil.toDIPFromPixel((float) width); + final float realHeight = PixelUtil.toDIPFromPixel((float) height); + ReadableMap currentState = this.mFabricViewStateManager.getStateData(); + if (currentState != null) { + float delta = 0.9F; + float stateFrameHeight = + currentState.hasKey("frameHeight") ? (float) currentState.getDouble("frameHeight") : 0.0F; + float stateFrameWidth = + currentState.hasKey("frameWidth") ? (float) currentState.getDouble("frameWidth") : 0.0F; + if (Math.abs(stateFrameWidth - realWidth) < delta + && Math.abs(stateFrameHeight - realHeight) < delta) { + return; } + } - this.mFabricViewStateManager.setState((StateUpdateCallback)(new StateUpdateCallback() { - public final WritableMap getStateUpdate() { - WritableMap map = (WritableMap)(new WritableNativeMap()); - map.putDouble("frameWidth", (double)realWidth); - map.putDouble("frameHeight", (double)realHeight); - return map; - } - })); - } + this.mFabricViewStateManager.setState( + (StateUpdateCallback) + (new StateUpdateCallback() { + public final WritableMap getStateUpdate() { + WritableMap map = (WritableMap) (new WritableNativeMap()); + map.putDouble("frameWidth", (double) realWidth); + map.putDouble("frameHeight", (double) realHeight); + return map; + } + })); + } - public FabricEnabledViewGroup(@Nullable ReactContext context) { - super((Context)context); - } + public FabricEnabledViewGroup(@Nullable ReactContext context) { + super((Context) context); + } } diff --git a/android/src/main/java/com/horcrux/svg/Brush.java b/android/src/main/java/com/horcrux/svg/Brush.java index 36c19175..0492cda3 100644 --- a/android/src/main/java/com/horcrux/svg/Brush.java +++ b/android/src/main/java/com/horcrux/svg/Brush.java @@ -6,7 +6,6 @@ * LICENSE file in the root directory of this source tree. */ - package com.horcrux.svg; import android.graphics.Bitmap; @@ -19,215 +18,214 @@ import android.graphics.RadialGradient; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Shader; - import com.facebook.common.logging.FLog; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.common.ReactConstants; class Brush { - private final BrushType mType; - private final SVGLength[] mPoints; - private ReadableArray mColors; - private final boolean mUseObjectBoundingBox; + private final BrushType mType; + private final SVGLength[] mPoints; + private ReadableArray mColors; + private final boolean mUseObjectBoundingBox; - // TODO implement pattern units - @SuppressWarnings({"unused"}) - private boolean mUseContentObjectBoundingBoxUnits; + // TODO implement pattern units + @SuppressWarnings({"unused"}) + private boolean mUseContentObjectBoundingBoxUnits; - private Matrix mMatrix; - private Rect mUserSpaceBoundingBox; - private PatternView mPattern; + private Matrix mMatrix; + private Rect mUserSpaceBoundingBox; + private PatternView mPattern; - Brush(BrushType type, SVGLength[] points, BrushUnits units) { - mType = type; - mPoints = points; - mUseObjectBoundingBox = units == BrushUnits.OBJECT_BOUNDING_BOX; + Brush(BrushType type, SVGLength[] points, BrushUnits units) { + mType = type; + mPoints = points; + mUseObjectBoundingBox = units == BrushUnits.OBJECT_BOUNDING_BOX; + } + + void setContentUnits(BrushUnits units) { + mUseContentObjectBoundingBoxUnits = units == BrushUnits.OBJECT_BOUNDING_BOX; + } + + void setPattern(PatternView pattern) { + mPattern = pattern; + } + + enum BrushType { + LINEAR_GRADIENT, + RADIAL_GRADIENT, + PATTERN + } + + enum BrushUnits { + OBJECT_BOUNDING_BOX, + USER_SPACE_ON_USE + } + + private static void parseGradientStops( + ReadableArray value, int stopsCount, float[] stops, int[] stopsColors, float opacity) { + for (int i = 0; i < stopsCount; i++) { + int stopIndex = i * 2; + stops[i] = (float) value.getDouble(stopIndex); + int color = value.getInt(stopIndex + 1); + int alpha = color >>> 24; + int combined = Math.round((float) alpha * opacity); + stopsColors[i] = combined << 24 | (color & 0x00ffffff); + } + } + + void setUserSpaceBoundingBox(Rect userSpaceBoundingBox) { + mUserSpaceBoundingBox = userSpaceBoundingBox; + } + + void setGradientColors(ReadableArray colors) { + mColors = colors; + } + + void setGradientTransform(Matrix matrix) { + mMatrix = matrix; + } + + private RectF getPaintRect(RectF pathBoundingBox) { + RectF rect = mUseObjectBoundingBox ? pathBoundingBox : new RectF(mUserSpaceBoundingBox); + float width = rect.width(); + float height = rect.height(); + float x = 0f; + float y = 0f; + + if (mUseObjectBoundingBox) { + x = rect.left; + y = rect.top; } - void setContentUnits(BrushUnits units) { - mUseContentObjectBoundingBoxUnits = units == BrushUnits.OBJECT_BOUNDING_BOX; + return new RectF(x, y, x + width, y + height); + } + + private double getVal(SVGLength length, double relative, float scale, float textSize) { + return PropHelper.fromRelative( + length, + relative, + 0, + mUseObjectBoundingBox && length.unit == SVGLength.UnitType.NUMBER ? relative : scale, + textSize); + } + + void setupPaint(Paint paint, RectF pathBoundingBox, float scale, float opacity) { + RectF rect = getPaintRect(pathBoundingBox); + float width = rect.width(); + float height = rect.height(); + float offsetX = rect.left; + float offsetY = rect.top; + + float textSize = paint.getTextSize(); + if (mType == BrushType.PATTERN) { + double x = getVal(mPoints[0], width, scale, textSize); + double y = getVal(mPoints[1], height, scale, textSize); + double w = getVal(mPoints[2], width, scale, textSize); + double h = getVal(mPoints[3], height, scale, textSize); + + if (!(w > 1 && h > 1)) { + return; + } + + Bitmap bitmap = Bitmap.createBitmap((int) w, (int) h, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + + RectF vbRect = mPattern.getViewBox(); + if (vbRect != null && vbRect.width() > 0 && vbRect.height() > 0) { + RectF eRect = new RectF((float) x, (float) y, (float) w, (float) h); + Matrix mViewBoxMatrix = + ViewBox.getTransform(vbRect, eRect, mPattern.mAlign, mPattern.mMeetOrSlice); + canvas.concat(mViewBoxMatrix); + } + + if (mUseContentObjectBoundingBoxUnits) { + canvas.scale(width / scale, height / scale); + } + + mPattern.draw(canvas, new Paint(), opacity); + + Matrix patternMatrix = new Matrix(); + if (mMatrix != null) { + patternMatrix.preConcat(mMatrix); + } + + BitmapShader bitmapShader = + new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT); + bitmapShader.setLocalMatrix(patternMatrix); + paint.setShader(bitmapShader); + return; } - void setPattern(PatternView pattern) { - mPattern = pattern; + int size = mColors.size(); + if (size == 0) { + FLog.w(ReactConstants.TAG, "Gradient contains no stops"); + return; + } + int stopsCount = size / 2; + int[] stopsColors = new int[stopsCount]; + float[] stops = new float[stopsCount]; + parseGradientStops(mColors, stopsCount, stops, stopsColors, opacity); + + if (stops.length == 1) { + // Gradient with only one stop will make LinearGradient/RadialGradient + // throw. It may happen when source SVG contains only one stop or + // two stops at the same spot (see lib/extract/extractGradient.js). + // Although it's mistake SVGs like this can be produced by vector + // editors or other tools, so let's handle that gracefully. + stopsColors = new int[] {stopsColors[0], stopsColors[0]}; + stops = new float[] {stops[0], stops[0]}; + FLog.w(ReactConstants.TAG, "Gradient contains only one stop"); } - enum BrushType { - LINEAR_GRADIENT, - RADIAL_GRADIENT, - PATTERN - } - - enum BrushUnits { - OBJECT_BOUNDING_BOX, - USER_SPACE_ON_USE - } - - private static void parseGradientStops(ReadableArray value, int stopsCount, float[] stops, int[] stopsColors, float opacity) { - for (int i = 0; i < stopsCount; i++) { - int stopIndex = i * 2; - stops[i] = (float) value.getDouble(stopIndex); - int color = value.getInt(stopIndex + 1); - int alpha = color >>> 24; - int combined = Math.round((float)alpha * opacity); - stopsColors[i] = combined << 24 | (color & 0x00ffffff); - } - } - - void setUserSpaceBoundingBox(Rect userSpaceBoundingBox) { - mUserSpaceBoundingBox = userSpaceBoundingBox; - } - - void setGradientColors(ReadableArray colors) { - mColors = colors; - } - - void setGradientTransform(Matrix matrix) { - mMatrix = matrix; - } - - private RectF getPaintRect(RectF pathBoundingBox) { - RectF rect = mUseObjectBoundingBox ? pathBoundingBox : new RectF(mUserSpaceBoundingBox); - float width = rect.width(); - float height = rect.height(); - float x = 0f; - float y = 0f; - - if (mUseObjectBoundingBox) { - x = rect.left; - y = rect.top; - } - - return new RectF(x, y, x + width, y + height); - } - - private double getVal(SVGLength length, double relative, float scale, float textSize) { - return PropHelper.fromRelative(length, relative, 0, mUseObjectBoundingBox && - length.unit == SVGLength.UnitType.NUMBER ? relative : scale, textSize); - } - - void setupPaint(Paint paint, RectF pathBoundingBox, float scale, float opacity) { - RectF rect = getPaintRect(pathBoundingBox); - float width = rect.width(); - float height = rect.height(); - float offsetX = rect.left; - float offsetY = rect.top; - - float textSize = paint.getTextSize(); - if (mType == BrushType.PATTERN) { - double x = getVal(mPoints[0], width, scale, textSize); - double y = getVal(mPoints[1], height, scale, textSize); - double w = getVal(mPoints[2], width, scale, textSize); - double h = getVal(mPoints[3], height, scale, textSize); - - if (!(w > 1 && h > 1)) { - return; - } - - Bitmap bitmap = Bitmap.createBitmap( - (int) w, - (int) h, - Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(bitmap); - - RectF vbRect = mPattern.getViewBox(); - if (vbRect != null && vbRect.width() > 0 && vbRect.height() > 0) { - RectF eRect = new RectF((float) x, (float) y, (float) w, (float) h); - Matrix mViewBoxMatrix = ViewBox.getTransform(vbRect, eRect, mPattern.mAlign, mPattern.mMeetOrSlice); - canvas.concat(mViewBoxMatrix); - } - - if (mUseContentObjectBoundingBoxUnits) { - canvas.scale(width / scale, height / scale); - } - - mPattern.draw(canvas, new Paint(), opacity); - - Matrix patternMatrix = new Matrix(); - if (mMatrix != null) { - patternMatrix.preConcat(mMatrix); - } - - BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT); - bitmapShader.setLocalMatrix(patternMatrix); - paint.setShader(bitmapShader); - return; - } - - int size = mColors.size(); - if (size == 0) { - FLog.w(ReactConstants.TAG, "Gradient contains no stops"); - return; - } - int stopsCount = size / 2; - int[] stopsColors = new int[stopsCount]; - float[] stops = new float[stopsCount]; - parseGradientStops(mColors, stopsCount, stops, stopsColors, opacity); - - if (stops.length == 1) { - // Gradient with only one stop will make LinearGradient/RadialGradient - // throw. It may happen when source SVG contains only one stop or - // two stops at the same spot (see lib/extract/extractGradient.js). - // Although it's mistake SVGs like this can be produced by vector - // editors or other tools, so let's handle that gracefully. - stopsColors = new int[] { stopsColors[0], stopsColors[0] }; - stops = new float[] { stops[0], stops[0] }; - FLog.w(ReactConstants.TAG, "Gradient contains only one stop"); - } - - if (mType == BrushType.LINEAR_GRADIENT) { - double x1 = getVal(mPoints[0], width, scale, textSize) + offsetX; - double y1 = getVal(mPoints[1], height, scale, textSize) + offsetY; - double x2 = getVal(mPoints[2], width, scale, textSize) + offsetX; - double y2 = getVal(mPoints[3], height, scale, textSize) + offsetY; - - Shader linearGradient = new LinearGradient( - (float) x1, - (float) y1, - (float) x2, - (float) y2, - stopsColors, - stops, - Shader.TileMode.CLAMP); - - if (mMatrix != null) { - Matrix m = new Matrix(); - m.preConcat(mMatrix); - linearGradient.setLocalMatrix(m); - } - - paint.setShader(linearGradient); - } else if (mType == BrushType.RADIAL_GRADIENT) { - double rx = getVal(mPoints[2], width, scale, textSize); - double ry = getVal(mPoints[3], height, scale, textSize); - - double ratio = ry / rx; - - double cx = getVal(mPoints[4], width, scale, textSize) + offsetX; - double cy = getVal(mPoints[5], height / ratio, scale, textSize) + offsetY / ratio; - - // TODO: support focus point. - //double fx = PropHelper.fromRelative(mPoints[0], width, offsetX, scale); - //double fy = PropHelper.fromRelative(mPoints[1], height, offsetY, scale) / (ry / rx); - - Shader radialGradient = new RadialGradient( - (float) cx, - (float) cy, - (float) rx, - stopsColors, - stops, - Shader.TileMode.CLAMP - ); - - Matrix radialMatrix = new Matrix(); - radialMatrix.preScale(1f, (float) ratio); - - if (mMatrix != null) { - radialMatrix.preConcat(mMatrix); - } - - radialGradient.setLocalMatrix(radialMatrix); - paint.setShader(radialGradient); - } + if (mType == BrushType.LINEAR_GRADIENT) { + double x1 = getVal(mPoints[0], width, scale, textSize) + offsetX; + double y1 = getVal(mPoints[1], height, scale, textSize) + offsetY; + double x2 = getVal(mPoints[2], width, scale, textSize) + offsetX; + double y2 = getVal(mPoints[3], height, scale, textSize) + offsetY; + + Shader linearGradient = + new LinearGradient( + (float) x1, + (float) y1, + (float) x2, + (float) y2, + stopsColors, + stops, + Shader.TileMode.CLAMP); + + if (mMatrix != null) { + Matrix m = new Matrix(); + m.preConcat(mMatrix); + linearGradient.setLocalMatrix(m); + } + + paint.setShader(linearGradient); + } else if (mType == BrushType.RADIAL_GRADIENT) { + double rx = getVal(mPoints[2], width, scale, textSize); + double ry = getVal(mPoints[3], height, scale, textSize); + + double ratio = ry / rx; + + double cx = getVal(mPoints[4], width, scale, textSize) + offsetX; + double cy = getVal(mPoints[5], height / ratio, scale, textSize) + offsetY / ratio; + + // TODO: support focus point. + // double fx = PropHelper.fromRelative(mPoints[0], width, offsetX, scale); + // double fy = PropHelper.fromRelative(mPoints[1], height, offsetY, scale) / (ry / rx); + + Shader radialGradient = + new RadialGradient( + (float) cx, (float) cy, (float) rx, stopsColors, stops, Shader.TileMode.CLAMP); + + Matrix radialMatrix = new Matrix(); + radialMatrix.preScale(1f, (float) ratio); + + if (mMatrix != null) { + radialMatrix.preConcat(mMatrix); + } + + radialGradient.setLocalMatrix(radialMatrix); + paint.setShader(radialGradient); } + } } diff --git a/android/src/main/java/com/horcrux/svg/CircleView.java b/android/src/main/java/com/horcrux/svg/CircleView.java index 33f37aec..ff7aaa73 100644 --- a/android/src/main/java/com/horcrux/svg/CircleView.java +++ b/android/src/main/java/com/horcrux/svg/CircleView.java @@ -6,71 +6,68 @@ * LICENSE file in the root directory of this source tree. */ - package com.horcrux.svg; - import android.annotation.SuppressLint; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; - import com.facebook.react.bridge.Dynamic; import com.facebook.react.bridge.ReactContext; import com.facebook.react.uimanager.annotations.ReactProp; @SuppressLint("ViewConstructor") class CircleView extends RenderableView { - private SVGLength mCx; - private SVGLength mCy; - private SVGLength mR; + private SVGLength mCx; + private SVGLength mCy; + private SVGLength mR; - public CircleView(ReactContext reactContext) { - super(reactContext); - } + public CircleView(ReactContext reactContext) { + super(reactContext); + } - @ReactProp(name = "cx") - public void setCx(Dynamic cx) { - mCx = SVGLength.from(cx); - invalidate(); - } + @ReactProp(name = "cx") + public void setCx(Dynamic cx) { + mCx = SVGLength.from(cx); + invalidate(); + } public void setCx(String cx) { mCx = SVGLength.from(cx); invalidate(); } - @ReactProp(name = "cy") - public void setCy(Dynamic cy) { - mCy = SVGLength.from(cy); - invalidate(); - } + @ReactProp(name = "cy") + public void setCy(Dynamic cy) { + mCy = SVGLength.from(cy); + invalidate(); + } public void setCy(String cy) { mCy = SVGLength.from(cy); invalidate(); } - @ReactProp(name = "r") - public void setR(Dynamic r) { - mR = SVGLength.from(r); - invalidate(); - } + @ReactProp(name = "r") + public void setR(Dynamic r) { + mR = SVGLength.from(r); + invalidate(); + } public void setR(String r) { mR = SVGLength.from(r); invalidate(); } - @Override - Path getPath(Canvas canvas, Paint paint) { - Path path = new Path(); + @Override + Path getPath(Canvas canvas, Paint paint) { + Path path = new Path(); - double cx = relativeOnWidth(mCx); - double cy = relativeOnHeight(mCy); - double r = relativeOnOther(mR); + double cx = relativeOnWidth(mCx); + double cy = relativeOnHeight(mCy); + double r = relativeOnOther(mR); - path.addCircle((float) cx, (float) cy, (float) r, Path.Direction.CW); - return path; - } + path.addCircle((float) cx, (float) cy, (float) r, Path.Direction.CW); + return path; + } } diff --git a/android/src/main/java/com/horcrux/svg/ClipPathView.java b/android/src/main/java/com/horcrux/svg/ClipPathView.java index cba5fb20..a8189167 100644 --- a/android/src/main/java/com/horcrux/svg/ClipPathView.java +++ b/android/src/main/java/com/horcrux/svg/ClipPathView.java @@ -6,13 +6,11 @@ * LICENSE file in the root directory of this source tree. */ - package com.horcrux.svg; import android.annotation.SuppressLint; import android.graphics.Canvas; import android.graphics.Paint; - import com.facebook.common.logging.FLog; import com.facebook.react.bridge.ReactContext; import com.facebook.react.common.ReactConstants; @@ -20,33 +18,35 @@ import com.facebook.react.common.ReactConstants; @SuppressLint("ViewConstructor") class ClipPathView extends GroupView { - public ClipPathView(ReactContext reactContext) { - super(reactContext); - } + public ClipPathView(ReactContext reactContext) { + super(reactContext); + } - @Override - void draw(Canvas canvas, Paint paint, float opacity) { - FLog.w(ReactConstants.TAG, "RNSVG: ClipPath can't be drawn, it should be defined as a child component for `Defs` "); - } + @Override + void draw(Canvas canvas, Paint paint, float opacity) { + FLog.w( + ReactConstants.TAG, + "RNSVG: ClipPath can't be drawn, it should be defined as a child component for `Defs` "); + } - @Override - void saveDefinition() { - getSvgView().defineClipPath(this, mName); - } + @Override + void saveDefinition() { + getSvgView().defineClipPath(this, mName); + } - @Override - boolean isResponsible() { - return false; - } + @Override + boolean isResponsible() { + return false; + } - @Override - int hitTest(float[] src) { - return -1; - } + @Override + int hitTest(float[] src) { + return -1; + } - @Override - void mergeProperties(RenderableView target) {} + @Override + void mergeProperties(RenderableView target) {} - @Override - void resetProperties() {} + @Override + void resetProperties() {} } diff --git a/android/src/main/java/com/horcrux/svg/DefinitionView.java b/android/src/main/java/com/horcrux/svg/DefinitionView.java index 28d1e28f..ddc8a71f 100644 --- a/android/src/main/java/com/horcrux/svg/DefinitionView.java +++ b/android/src/main/java/com/horcrux/svg/DefinitionView.java @@ -6,38 +6,36 @@ * LICENSE file in the root directory of this source tree. */ - package com.horcrux.svg; import android.annotation.SuppressLint; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; - import com.facebook.react.bridge.ReactContext; @SuppressLint("ViewConstructor") class DefinitionView extends VirtualView { - DefinitionView(ReactContext reactContext) { - super(reactContext); - } + DefinitionView(ReactContext reactContext) { + super(reactContext); + } - @SuppressWarnings("EmptyMethod") - void draw(Canvas canvas, Paint paint, float opacity) {} + @SuppressWarnings("EmptyMethod") + void draw(Canvas canvas, Paint paint, float opacity) {} - @Override - boolean isResponsible() { - return false; - } + @Override + boolean isResponsible() { + return false; + } - @Override - Path getPath(Canvas canvas, Paint paint) { - return null; - } + @Override + Path getPath(Canvas canvas, Paint paint) { + return null; + } - @Override - int hitTest(float[] src) { - return -1; - } + @Override + int hitTest(float[] src) { + return -1; + } } diff --git a/android/src/main/java/com/horcrux/svg/DefsView.java b/android/src/main/java/com/horcrux/svg/DefsView.java index e3e031a6..7a4f4d8e 100644 --- a/android/src/main/java/com/horcrux/svg/DefsView.java +++ b/android/src/main/java/com/horcrux/svg/DefsView.java @@ -6,32 +6,30 @@ * LICENSE file in the root directory of this source tree. */ - package com.horcrux.svg; import android.annotation.SuppressLint; import android.graphics.Canvas; import android.graphics.Paint; import android.view.View; - import com.facebook.react.bridge.ReactContext; @SuppressLint("ViewConstructor") class DefsView extends DefinitionView { - public DefsView(ReactContext reactContext) { - super(reactContext); - } + public DefsView(ReactContext reactContext) { + super(reactContext); + } - @Override - void draw(Canvas canvas, Paint paint, float opacity) {} + @Override + void draw(Canvas canvas, Paint paint, float opacity) {} - void saveDefinition() { - for (int i = 0; i < getChildCount(); i++) { - View child = getChildAt(i); - if (child instanceof VirtualView) { - ((VirtualView)child).saveDefinition(); - } - } + void saveDefinition() { + for (int i = 0; i < getChildCount(); i++) { + View child = getChildAt(i); + if (child instanceof VirtualView) { + ((VirtualView) child).saveDefinition(); + } } + } } diff --git a/android/src/main/java/com/horcrux/svg/EllipseView.java b/android/src/main/java/com/horcrux/svg/EllipseView.java index dd450447..1ed2612d 100644 --- a/android/src/main/java/com/horcrux/svg/EllipseView.java +++ b/android/src/main/java/com/horcrux/svg/EllipseView.java @@ -6,7 +6,6 @@ * LICENSE file in the root directory of this source tree. */ - package com.horcrux.svg; import android.annotation.SuppressLint; @@ -14,76 +13,76 @@ import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; import android.graphics.RectF; - import com.facebook.react.bridge.Dynamic; import com.facebook.react.bridge.ReactContext; import com.facebook.react.uimanager.annotations.ReactProp; @SuppressLint("ViewConstructor") class EllipseView extends RenderableView { - private SVGLength mCx; - private SVGLength mCy; - private SVGLength mRx; - private SVGLength mRy; + private SVGLength mCx; + private SVGLength mCy; + private SVGLength mRx; + private SVGLength mRy; - public EllipseView(ReactContext reactContext) { - super(reactContext); - } + public EllipseView(ReactContext reactContext) { + super(reactContext); + } - @ReactProp(name = "cx") - public void setCx(Dynamic cx) { - mCx = SVGLength.from(cx); - invalidate(); - } + @ReactProp(name = "cx") + public void setCx(Dynamic cx) { + mCx = SVGLength.from(cx); + invalidate(); + } public void setCx(String cx) { mCx = SVGLength.from(cx); invalidate(); } - @ReactProp(name = "cy") - public void setCy(Dynamic cy) { - mCy = SVGLength.from(cy); - invalidate(); - } + @ReactProp(name = "cy") + public void setCy(Dynamic cy) { + mCy = SVGLength.from(cy); + invalidate(); + } public void setCy(String cy) { mCy = SVGLength.from(cy); invalidate(); } - @ReactProp(name = "rx") - public void setRx(Dynamic rx) { - mRx = SVGLength.from(rx); - invalidate(); - } + @ReactProp(name = "rx") + public void setRx(Dynamic rx) { + mRx = SVGLength.from(rx); + invalidate(); + } public void setRx(String rx) { mRx = SVGLength.from(rx); invalidate(); } - @ReactProp(name = "ry") - public void setRy(Dynamic ry) { - mRy = SVGLength.from(ry); - invalidate(); - } + @ReactProp(name = "ry") + public void setRy(Dynamic ry) { + mRy = SVGLength.from(ry); + invalidate(); + } public void setRy(String ry) { mRy = SVGLength.from(ry); invalidate(); } - @Override - Path getPath(Canvas canvas, Paint paint) { - Path path = new Path(); - double cx = relativeOnWidth(mCx); - double cy = relativeOnHeight(mCy); - double rx = relativeOnWidth(mRx); - double ry = relativeOnHeight(mRy); - RectF oval = new RectF((float) (cx - rx), (float) (cy - ry), (float) (cx + rx), (float) (cy + ry)); - path.addOval(oval, Path.Direction.CW); + @Override + Path getPath(Canvas canvas, Paint paint) { + Path path = new Path(); + double cx = relativeOnWidth(mCx); + double cy = relativeOnHeight(mCy); + double rx = relativeOnWidth(mRx); + double ry = relativeOnHeight(mRy); + RectF oval = + new RectF((float) (cx - rx), (float) (cy - ry), (float) (cx + rx), (float) (cy + ry)); + path.addOval(oval, Path.Direction.CW); - return path; - } + return path; + } } diff --git a/android/src/main/java/com/horcrux/svg/FontData.java b/android/src/main/java/com/horcrux/svg/FontData.java index 56a8b496..fb789068 100644 --- a/android/src/main/java/com/horcrux/svg/FontData.java +++ b/android/src/main/java/com/horcrux/svg/FontData.java @@ -1,214 +1,232 @@ package com.horcrux.svg; -import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.bridge.ReadableType; - import static com.facebook.react.uimanager.ViewProps.FONT_FAMILY; import static com.facebook.react.uimanager.ViewProps.FONT_SIZE; import static com.facebook.react.uimanager.ViewProps.FONT_STYLE; import static com.facebook.react.uimanager.ViewProps.FONT_WEIGHT; import static com.horcrux.svg.TextProperties.*; +import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.bridge.ReadableType; + class FontData { - static class AbsoluteFontWeight { + static class AbsoluteFontWeight { - static final int normal = 400; + static final int normal = 400; - private static final FontWeight[] WEIGHTS = new FontWeight[]{ - FontWeight.w100, - FontWeight.w100, - FontWeight.w200, - FontWeight.w300, - FontWeight.Normal, - FontWeight.w500, - FontWeight.w600, - FontWeight.Bold, - FontWeight.w800, - FontWeight.w900, - FontWeight.w900, + private static final FontWeight[] WEIGHTS = + new FontWeight[] { + FontWeight.w100, + FontWeight.w100, + FontWeight.w200, + FontWeight.w300, + FontWeight.Normal, + FontWeight.w500, + FontWeight.w600, + FontWeight.Bold, + FontWeight.w800, + FontWeight.w900, + FontWeight.w900, }; - static FontWeight nearestFontWeight(int absoluteFontWeight) { - return WEIGHTS[Math.round(absoluteFontWeight / 100f)]; - } - - private static final int[] absoluteFontWeights = new int[]{ - 400, 700, 100, 200, 300, 400, 500, 600, 700, 800, 900 - }; - - // https://drafts.csswg.org/css-fonts-4/#relative-weights - static int from(FontWeight fontWeight, FontData parent) { - if (fontWeight == FontWeight.Bolder) { - return bolder(parent.absoluteFontWeight); - } else if (fontWeight == FontWeight.Lighter) { - return lighter(parent.absoluteFontWeight); - } else { - return absoluteFontWeights[fontWeight.ordinal()]; - } - } - - private static int bolder(int inherited) { - if (inherited < 350) { - return 400; - } else if (inherited < 550) { - return 700; - } else if (inherited < 900) { - return 900; - } else { - return inherited; - } - } - - private static int lighter(int inherited) { - if (inherited < 100) { - return inherited; - } else if (inherited < 550) { - return 100; - } else if (inherited < 750) { - return 400; - } else { - return 700; - } - } + static FontWeight nearestFontWeight(int absoluteFontWeight) { + return WEIGHTS[Math.round(absoluteFontWeight / 100f)]; } - static final double DEFAULT_FONT_SIZE = 12d; + private static final int[] absoluteFontWeights = + new int[] {400, 700, 100, 200, 300, 400, 500, 600, 700, 800, 900}; - private static final double DEFAULT_KERNING = 0d; - private static final double DEFAULT_WORD_SPACING = 0d; - private static final double DEFAULT_LETTER_SPACING = 0d; - - private static final String KERNING = "kerning"; - private static final String FONT_DATA = "fontData"; - private static final String TEXT_ANCHOR = "textAnchor"; - private static final String WORD_SPACING = "wordSpacing"; - private static final String LETTER_SPACING = "letterSpacing"; - private static final String TEXT_DECORATION = "textDecoration"; - private static final String FONT_FEATURE_SETTINGS = "fontFeatureSettings"; - private static final String FONT_VARIATION_SETTINGS = "fontVariationSettings"; - private static final String FONT_VARIANT_LIGATURES = "fontVariantLigatures"; - - final double fontSize; - final String fontFamily; - final FontStyle fontStyle; - final ReadableMap fontData; - - FontWeight fontWeight; - int absoluteFontWeight; - - final String fontFeatureSettings; - final String fontVariationSettings; - final FontVariantLigatures fontVariantLigatures; - - final TextAnchor textAnchor; - private final TextDecoration textDecoration; - - final double kerning; - final double wordSpacing; - final double letterSpacing; - - final boolean manualKerning; - - static final FontData Defaults = new FontData(); - - private FontData() { - fontData = null; - fontFamily = ""; - fontStyle = FontStyle.normal; - fontWeight = FontWeight.Normal; - absoluteFontWeight = AbsoluteFontWeight.normal; - fontFeatureSettings = ""; - fontVariationSettings = ""; - fontVariantLigatures = FontVariantLigatures.normal; - - textAnchor = TextAnchor.start; - textDecoration = TextDecoration.None; - - manualKerning = false; - kerning = DEFAULT_KERNING; - fontSize = DEFAULT_FONT_SIZE; - wordSpacing = DEFAULT_WORD_SPACING; - letterSpacing = DEFAULT_LETTER_SPACING; + // https://drafts.csswg.org/css-fonts-4/#relative-weights + static int from(FontWeight fontWeight, FontData parent) { + if (fontWeight == FontWeight.Bolder) { + return bolder(parent.absoluteFontWeight); + } else if (fontWeight == FontWeight.Lighter) { + return lighter(parent.absoluteFontWeight); + } else { + return absoluteFontWeights[fontWeight.ordinal()]; + } } - private double toAbsolute(ReadableMap font, String prop, double scale, double fontSize, double relative) { - ReadableType propType = font.getType(prop); - if (propType == ReadableType.Number) { - return font.getDouble(prop); + private static int bolder(int inherited) { + if (inherited < 350) { + return 400; + } else if (inherited < 550) { + return 700; + } else if (inherited < 900) { + return 900; + } else { + return inherited; + } + } + + private static int lighter(int inherited) { + if (inherited < 100) { + return inherited; + } else if (inherited < 550) { + return 100; + } else if (inherited < 750) { + return 400; + } else { + return 700; + } + } + } + + static final double DEFAULT_FONT_SIZE = 12d; + + private static final double DEFAULT_KERNING = 0d; + private static final double DEFAULT_WORD_SPACING = 0d; + private static final double DEFAULT_LETTER_SPACING = 0d; + + private static final String KERNING = "kerning"; + private static final String FONT_DATA = "fontData"; + private static final String TEXT_ANCHOR = "textAnchor"; + private static final String WORD_SPACING = "wordSpacing"; + private static final String LETTER_SPACING = "letterSpacing"; + private static final String TEXT_DECORATION = "textDecoration"; + private static final String FONT_FEATURE_SETTINGS = "fontFeatureSettings"; + private static final String FONT_VARIATION_SETTINGS = "fontVariationSettings"; + private static final String FONT_VARIANT_LIGATURES = "fontVariantLigatures"; + + final double fontSize; + final String fontFamily; + final FontStyle fontStyle; + final ReadableMap fontData; + + FontWeight fontWeight; + int absoluteFontWeight; + + final String fontFeatureSettings; + final String fontVariationSettings; + final FontVariantLigatures fontVariantLigatures; + + final TextAnchor textAnchor; + private final TextDecoration textDecoration; + + final double kerning; + final double wordSpacing; + final double letterSpacing; + + final boolean manualKerning; + + static final FontData Defaults = new FontData(); + + private FontData() { + fontData = null; + fontFamily = ""; + fontStyle = FontStyle.normal; + fontWeight = FontWeight.Normal; + absoluteFontWeight = AbsoluteFontWeight.normal; + fontFeatureSettings = ""; + fontVariationSettings = ""; + fontVariantLigatures = FontVariantLigatures.normal; + + textAnchor = TextAnchor.start; + textDecoration = TextDecoration.None; + + manualKerning = false; + kerning = DEFAULT_KERNING; + fontSize = DEFAULT_FONT_SIZE; + wordSpacing = DEFAULT_WORD_SPACING; + letterSpacing = DEFAULT_LETTER_SPACING; + } + + private double toAbsolute( + ReadableMap font, String prop, double scale, double fontSize, double relative) { + ReadableType propType = font.getType(prop); + if (propType == ReadableType.Number) { + return font.getDouble(prop); + } else { + String string = font.getString(prop); + return PropHelper.fromRelative(string, relative, scale, fontSize); + } + } + + private void setInheritedWeight(FontData parent) { + absoluteFontWeight = parent.absoluteFontWeight; + fontWeight = parent.fontWeight; + } + + private void handleNumericWeight(FontData parent, double number) { + long weight = Math.round(number); + if (weight >= 1 && weight <= 1000) { + absoluteFontWeight = (int) weight; + fontWeight = AbsoluteFontWeight.nearestFontWeight(absoluteFontWeight); + } else { + setInheritedWeight(parent); + } + } + + FontData(ReadableMap font, FontData parent, double scale) { + double parentFontSize = parent.fontSize; + + if (font.hasKey(FONT_SIZE)) { + fontSize = toAbsolute(font, FONT_SIZE, 1, parentFontSize, parentFontSize); + } else { + fontSize = parentFontSize; + } + + if (font.hasKey(FONT_WEIGHT)) { + ReadableType fontWeightType = font.getType(FONT_WEIGHT); + if (fontWeightType == ReadableType.Number) { + handleNumericWeight(parent, font.getDouble(FONT_WEIGHT)); + } else { + String string = font.getString(FONT_WEIGHT); + if (FontWeight.hasEnum(string)) { + absoluteFontWeight = AbsoluteFontWeight.from(FontWeight.get(string), parent); + fontWeight = AbsoluteFontWeight.nearestFontWeight(absoluteFontWeight); + } else if (string != null) { + handleNumericWeight(parent, Double.parseDouble(string)); } else { - String string = font.getString(prop); - return PropHelper.fromRelative( - string, - relative, - scale, - fontSize - ); + setInheritedWeight(parent); } + } + } else { + setInheritedWeight(parent); } - private void setInheritedWeight(FontData parent) { - absoluteFontWeight = parent.absoluteFontWeight; - fontWeight = parent.fontWeight; - } + fontData = font.hasKey(FONT_DATA) ? font.getMap(FONT_DATA) : parent.fontData; - private void handleNumericWeight(FontData parent, double number) { - long weight = Math.round(number); - if (weight >= 1 && weight <= 1000) { - absoluteFontWeight = (int)weight; - fontWeight = AbsoluteFontWeight.nearestFontWeight(absoluteFontWeight); - } else { - setInheritedWeight(parent); - } - } + fontFamily = font.hasKey(FONT_FAMILY) ? font.getString(FONT_FAMILY) : parent.fontFamily; + fontStyle = + font.hasKey(FONT_STYLE) ? FontStyle.valueOf(font.getString(FONT_STYLE)) : parent.fontStyle; + fontFeatureSettings = + font.hasKey(FONT_FEATURE_SETTINGS) + ? font.getString(FONT_FEATURE_SETTINGS) + : parent.fontFeatureSettings; + fontVariationSettings = + font.hasKey(FONT_VARIATION_SETTINGS) + ? font.getString(FONT_VARIATION_SETTINGS) + : parent.fontVariationSettings; + fontVariantLigatures = + font.hasKey(FONT_VARIANT_LIGATURES) + ? FontVariantLigatures.valueOf(font.getString(FONT_VARIANT_LIGATURES)) + : parent.fontVariantLigatures; - FontData(ReadableMap font, FontData parent, double scale) { - double parentFontSize = parent.fontSize; + textAnchor = + font.hasKey(TEXT_ANCHOR) + ? TextAnchor.valueOf(font.getString(TEXT_ANCHOR)) + : parent.textAnchor; + textDecoration = + font.hasKey(TEXT_DECORATION) + ? TextDecoration.getEnum(font.getString(TEXT_DECORATION)) + : parent.textDecoration; - if (font.hasKey(FONT_SIZE)) { - fontSize = toAbsolute(font, FONT_SIZE, 1, parentFontSize, parentFontSize); - } else { - fontSize = parentFontSize; - } + final boolean hasKerning = font.hasKey(KERNING); + manualKerning = hasKerning || parent.manualKerning; - if (font.hasKey(FONT_WEIGHT)) { - ReadableType fontWeightType = font.getType(FONT_WEIGHT); - if (fontWeightType == ReadableType.Number) { - handleNumericWeight(parent, font.getDouble(FONT_WEIGHT)); - } else { - String string = font.getString(FONT_WEIGHT); - if (FontWeight.hasEnum(string)) { - absoluteFontWeight = AbsoluteFontWeight.from(FontWeight.get(string), parent); - fontWeight = AbsoluteFontWeight.nearestFontWeight(absoluteFontWeight); - } else if (string != null) { - handleNumericWeight(parent, Double.parseDouble(string)); - } else { - setInheritedWeight(parent); - } - } - } else { - setInheritedWeight(parent); - } - - fontData = font.hasKey(FONT_DATA) ? font.getMap(FONT_DATA) : parent.fontData; - - fontFamily = font.hasKey(FONT_FAMILY) ? font.getString(FONT_FAMILY) : parent.fontFamily; - fontStyle = font.hasKey(FONT_STYLE) ? FontStyle.valueOf(font.getString(FONT_STYLE)) : parent.fontStyle; - fontFeatureSettings = font.hasKey(FONT_FEATURE_SETTINGS) ? font.getString(FONT_FEATURE_SETTINGS) : parent.fontFeatureSettings; - fontVariationSettings = font.hasKey(FONT_VARIATION_SETTINGS) ? font.getString(FONT_VARIATION_SETTINGS) : parent.fontVariationSettings; - fontVariantLigatures = font.hasKey(FONT_VARIANT_LIGATURES) ? FontVariantLigatures.valueOf(font.getString(FONT_VARIANT_LIGATURES)) : parent.fontVariantLigatures; - - textAnchor = font.hasKey(TEXT_ANCHOR) ? TextAnchor.valueOf(font.getString(TEXT_ANCHOR)) : parent.textAnchor; - textDecoration = font.hasKey(TEXT_DECORATION) ? TextDecoration.getEnum(font.getString(TEXT_DECORATION)) : parent.textDecoration; - - final boolean hasKerning = font.hasKey(KERNING); - manualKerning = hasKerning || parent.manualKerning; - - // https://www.w3.org/TR/SVG11/text.html#SpacingProperties - // https://drafts.csswg.org/css-text-3/#spacing - // calculated values for units in: kerning, word-spacing, and, letter-spacing. - kerning = hasKerning ? toAbsolute(font, KERNING, scale, fontSize, 0) : parent.kerning; - wordSpacing = font.hasKey(WORD_SPACING) ? toAbsolute(font, WORD_SPACING, scale, fontSize, 0) : parent.wordSpacing; - letterSpacing = font.hasKey(LETTER_SPACING) ? toAbsolute(font, LETTER_SPACING, scale, fontSize, 0) : parent.letterSpacing; - } + // https://www.w3.org/TR/SVG11/text.html#SpacingProperties + // https://drafts.csswg.org/css-text-3/#spacing + // calculated values for units in: kerning, word-spacing, and, letter-spacing. + kerning = hasKerning ? toAbsolute(font, KERNING, scale, fontSize, 0) : parent.kerning; + wordSpacing = + font.hasKey(WORD_SPACING) + ? toAbsolute(font, WORD_SPACING, scale, fontSize, 0) + : parent.wordSpacing; + letterSpacing = + font.hasKey(LETTER_SPACING) + ? toAbsolute(font, LETTER_SPACING, scale, fontSize, 0) + : parent.letterSpacing; + } } diff --git a/android/src/main/java/com/horcrux/svg/ForeignObjectView.java b/android/src/main/java/com/horcrux/svg/ForeignObjectView.java index 1679d5f4..6baf1e40 100644 --- a/android/src/main/java/com/horcrux/svg/ForeignObjectView.java +++ b/android/src/main/java/com/horcrux/svg/ForeignObjectView.java @@ -6,7 +6,6 @@ * LICENSE file in the root directory of this source tree. */ - package com.horcrux.svg; import android.annotation.SuppressLint; @@ -15,9 +14,7 @@ import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.RectF; import android.view.View; - import androidx.annotation.NonNull; - import com.facebook.react.bridge.Dynamic; import com.facebook.react.bridge.ReactContext; import com.facebook.react.uimanager.annotations.ReactProp; @@ -25,136 +22,136 @@ import com.facebook.react.uimanager.annotations.ReactProp; @SuppressLint("ViewConstructor") class ForeignObjectView extends GroupView { - SVGLength mX; - SVGLength mY; - SVGLength mW; - SVGLength mH; + SVGLength mX; + SVGLength mY; + SVGLength mW; + SVGLength mH; - public ForeignObjectView(ReactContext reactContext) { - super(reactContext); - } + public ForeignObjectView(ReactContext reactContext) { + super(reactContext); + } - @Override - void draw(Canvas canvas, Paint paint, float opacity) { - float x = (float)relativeOnWidth(mX); - float y = (float)relativeOnHeight(mY); - float w = (float)relativeOnWidth(mW); - float h = (float)relativeOnHeight(mH); - canvas.translate(x, y); - canvas.clipRect(0, 0, w, h); - super.draw(canvas, paint, opacity); - } + @Override + void draw(Canvas canvas, Paint paint, float opacity) { + float x = (float) relativeOnWidth(mX); + float y = (float) relativeOnHeight(mY); + float w = (float) relativeOnWidth(mW); + float h = (float) relativeOnHeight(mH); + canvas.translate(x, y); + canvas.clipRect(0, 0, w, h); + super.draw(canvas, paint, opacity); + } - @Override - public void onDescendantInvalidated(@NonNull View child, @NonNull View target) { - super.onDescendantInvalidated(child, target); - invalidate(); - } + @Override + public void onDescendantInvalidated(@NonNull View child, @NonNull View target) { + super.onDescendantInvalidated(child, target); + invalidate(); + } - @ReactProp(name = "x") - public void setX(Dynamic x) { - mX = SVGLength.from(x); - invalidate(); - } + @ReactProp(name = "x") + public void setX(Dynamic x) { + mX = SVGLength.from(x); + invalidate(); + } public void setX(String x) { mX = SVGLength.from(x); invalidate(); } - @ReactProp(name = "y") - public void setY(Dynamic y) { - mY = SVGLength.from(y); - invalidate(); - } + @ReactProp(name = "y") + public void setY(Dynamic y) { + mY = SVGLength.from(y); + invalidate(); + } public void setY(String y) { mY = SVGLength.from(y); invalidate(); } - @ReactProp(name = "width") - public void setWidth(Dynamic width) { - mW = SVGLength.from(width); - invalidate(); - } + @ReactProp(name = "width") + public void setWidth(Dynamic width) { + mW = SVGLength.from(width); + invalidate(); + } public void setWidth(String width) { mW = SVGLength.from(width); invalidate(); } - @ReactProp(name = "height") - public void setHeight(Dynamic height) { - mH = SVGLength.from(height); - invalidate(); - } + @ReactProp(name = "height") + public void setHeight(Dynamic height) { + mH = SVGLength.from(height); + invalidate(); + } public void setHeight(String height) { mH = SVGLength.from(height); invalidate(); } - void drawGroup(final Canvas canvas, final Paint paint, final float opacity) { - pushGlyphContext(); - final SvgView svg = getSvgView(); - final GroupView self = this; - final RectF groupRect = new RectF(); - for (int i = 0; i < getChildCount(); i++) { - View child = getChildAt(i); - if (child instanceof MaskView) { - continue; - } - if (child instanceof VirtualView) { - VirtualView node = ((VirtualView)child); - if ("none".equals(node.mDisplay)) { - continue; - } - if (node instanceof RenderableView) { - ((RenderableView)node).mergeProperties(self); - } - - int count = node.saveAndSetupCanvas(canvas, mCTM); - node.render(canvas, paint, opacity * mOpacity); - RectF r = node.getClientRect(); - if (r != null) { - groupRect.union(r); - } - - node.restoreCanvas(canvas, count); - - if (node instanceof RenderableView) { - ((RenderableView)node).resetProperties(); - } - - if (node.isResponsible()) { - svg.enableTouchEvents(); - } - } else if (child instanceof SvgView) { - SvgView svgView = (SvgView)child; - svgView.drawChildren(canvas); - if (svgView.isResponsible()) { - svg.enableTouchEvents(); - } - } else { - // Enable rendering other native ancestor views in e.g. masks - child.draw(canvas); - } + void drawGroup(final Canvas canvas, final Paint paint, final float opacity) { + pushGlyphContext(); + final SvgView svg = getSvgView(); + final GroupView self = this; + final RectF groupRect = new RectF(); + for (int i = 0; i < getChildCount(); i++) { + View child = getChildAt(i); + if (child instanceof MaskView) { + continue; + } + if (child instanceof VirtualView) { + VirtualView node = ((VirtualView) child); + if ("none".equals(node.mDisplay)) { + continue; + } + if (node instanceof RenderableView) { + ((RenderableView) node).mergeProperties(self); } - this.setClientRect(groupRect); - popGlyphContext(); - } - // Enable rendering other native ancestor views in e.g. masks, but don't render them another time - Bitmap fakeBitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); - Canvas fake = new Canvas(fakeBitmap); + int count = node.saveAndSetupCanvas(canvas, mCTM); + node.render(canvas, paint, opacity * mOpacity); + RectF r = node.getClientRect(); + if (r != null) { + groupRect.union(r); + } - @Override - protected void dispatchDraw(Canvas canvas) { - super.dispatchDraw(fake); - } + node.restoreCanvas(canvas, count); - protected boolean drawChild(Canvas canvas, View child, long drawingTime) { - return super.drawChild(fake, child, drawingTime); + if (node instanceof RenderableView) { + ((RenderableView) node).resetProperties(); + } + + if (node.isResponsible()) { + svg.enableTouchEvents(); + } + } else if (child instanceof SvgView) { + SvgView svgView = (SvgView) child; + svgView.drawChildren(canvas); + if (svgView.isResponsible()) { + svg.enableTouchEvents(); + } + } else { + // Enable rendering other native ancestor views in e.g. masks + child.draw(canvas); + } } + this.setClientRect(groupRect); + popGlyphContext(); + } + + // Enable rendering other native ancestor views in e.g. masks, but don't render them another time + Bitmap fakeBitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); + Canvas fake = new Canvas(fakeBitmap); + + @Override + protected void dispatchDraw(Canvas canvas) { + super.dispatchDraw(fake); + } + + protected boolean drawChild(Canvas canvas, View child, long drawingTime) { + return super.drawChild(fake, child, drawingTime); + } } diff --git a/android/src/main/java/com/horcrux/svg/GlyphContext.java b/android/src/main/java/com/horcrux/svg/GlyphContext.java index d42bff93..9de43aa3 100644 --- a/android/src/main/java/com/horcrux/svg/GlyphContext.java +++ b/android/src/main/java/com/horcrux/svg/GlyphContext.java @@ -6,423 +6,410 @@ * LICENSE file in the root directory of this source tree. */ - package com.horcrux.svg; import com.facebook.react.bridge.ReadableMap; - import java.util.ArrayList; - import javax.annotation.Nullable; // https://www.w3.org/TR/SVG/text.html#TSpanElement class GlyphContext { - // Current stack (one per node push/pop) - final ArrayList mFontContext = new ArrayList<>(); + // Current stack (one per node push/pop) + final ArrayList mFontContext = new ArrayList<>(); - // Unique input attribute lists (only added if node sets a value) - private final ArrayList mXsContext = new ArrayList<>(); - private final ArrayList mYsContext = new ArrayList<>(); - private final ArrayList mDXsContext = new ArrayList<>(); - private final ArrayList mDYsContext = new ArrayList<>(); - private final ArrayList mRsContext = new ArrayList<>(); + // Unique input attribute lists (only added if node sets a value) + private final ArrayList mXsContext = new ArrayList<>(); + private final ArrayList mYsContext = new ArrayList<>(); + private final ArrayList mDXsContext = new ArrayList<>(); + private final ArrayList mDYsContext = new ArrayList<>(); + private final ArrayList mRsContext = new ArrayList<>(); - // Unique index into attribute list (one per unique list) - private final ArrayList mXIndices = new ArrayList<>(); - private final ArrayList mYIndices = new ArrayList<>(); - private final ArrayList mDXIndices = new ArrayList<>(); - private final ArrayList mDYIndices = new ArrayList<>(); - private final ArrayList mRIndices = new ArrayList<>(); + // Unique index into attribute list (one per unique list) + private final ArrayList mXIndices = new ArrayList<>(); + private final ArrayList mYIndices = new ArrayList<>(); + private final ArrayList mDXIndices = new ArrayList<>(); + private final ArrayList mDYIndices = new ArrayList<>(); + private final ArrayList mRIndices = new ArrayList<>(); - // Index of unique context used (one per node push/pop) - private final ArrayList mXsIndices = new ArrayList<>(); - private final ArrayList mYsIndices = new ArrayList<>(); - private final ArrayList mDXsIndices = new ArrayList<>(); - private final ArrayList mDYsIndices = new ArrayList<>(); - private final ArrayList mRsIndices = new ArrayList<>(); + // Index of unique context used (one per node push/pop) + private final ArrayList mXsIndices = new ArrayList<>(); + private final ArrayList mYsIndices = new ArrayList<>(); + private final ArrayList mDXsIndices = new ArrayList<>(); + private final ArrayList mDYsIndices = new ArrayList<>(); + private final ArrayList mRsIndices = new ArrayList<>(); - // Calculated on push context, percentage and em length depends on parent font size - private double mFontSize = FontData.DEFAULT_FONT_SIZE; - private FontData topFont = FontData.Defaults; + // Calculated on push context, percentage and em length depends on parent font size + private double mFontSize = FontData.DEFAULT_FONT_SIZE; + private FontData topFont = FontData.Defaults; - // Current accumulated values - // https://www.w3.org/TR/SVG/types.html#DataTypeCoordinate - // syntax is the same as that for - private double mX; - private double mY; + // Current accumulated values + // https://www.w3.org/TR/SVG/types.html#DataTypeCoordinate + // syntax is the same as that for + private double mX; + private double mY; - // https://www.w3.org/TR/SVG/types.html#Length - private double mDX; - private double mDY; + // https://www.w3.org/TR/SVG/types.html#Length + private double mDX; + private double mDY; - // Current SVGLengthList - // https://www.w3.org/TR/SVG/types.html#InterfaceSVGLengthList - // https://www.w3.org/TR/SVG/types.html#DataTypeCoordinates + // Current SVGLengthList + // https://www.w3.org/TR/SVG/types.html#InterfaceSVGLengthList + // https://www.w3.org/TR/SVG/types.html#DataTypeCoordinates - // https://www.w3.org/TR/SVG/text.html#TSpanElementXAttribute - private SVGLength[] mXs = new SVGLength[]{}; + // https://www.w3.org/TR/SVG/text.html#TSpanElementXAttribute + private SVGLength[] mXs = new SVGLength[] {}; - // https://www.w3.org/TR/SVG/text.html#TSpanElementYAttribute - private SVGLength[] mYs = new SVGLength[]{}; + // https://www.w3.org/TR/SVG/text.html#TSpanElementYAttribute + private SVGLength[] mYs = new SVGLength[] {}; - // Current SVGLengthList - // https://www.w3.org/TR/SVG/types.html#DataTypeLengths + // Current SVGLengthList + // https://www.w3.org/TR/SVG/types.html#DataTypeLengths - // https://www.w3.org/TR/SVG/text.html#TSpanElementDXAttribute - private SVGLength[] mDXs = new SVGLength[]{}; + // https://www.w3.org/TR/SVG/text.html#TSpanElementDXAttribute + private SVGLength[] mDXs = new SVGLength[] {}; - // https://www.w3.org/TR/SVG/text.html#TSpanElementDYAttribute - private SVGLength[] mDYs = new SVGLength[]{}; + // https://www.w3.org/TR/SVG/text.html#TSpanElementDYAttribute + private SVGLength[] mDYs = new SVGLength[] {}; - // Current SVGLengthList - // https://www.w3.org/TR/SVG/types.html#DataTypeNumbers + // Current SVGLengthList + // https://www.w3.org/TR/SVG/types.html#DataTypeNumbers - // https://www.w3.org/TR/SVG/text.html#TSpanElementRotateAttribute - private double[] mRs = new double[]{0}; + // https://www.w3.org/TR/SVG/text.html#TSpanElementRotateAttribute + private double[] mRs = new double[] {0}; - // Current attribute list index - private int mXsIndex; - private int mYsIndex; - private int mDXsIndex; - private int mDYsIndex; - private int mRsIndex; + // Current attribute list index + private int mXsIndex; + private int mYsIndex; + private int mDXsIndex; + private int mDYsIndex; + private int mRsIndex; - // Current value index in current attribute list - private int mXIndex = -1; - private int mYIndex = -1; - private int mDXIndex = -1; - private int mDYIndex = -1; - private int mRIndex = -1; + // Current value index in current attribute list + private int mXIndex = -1; + private int mYIndex = -1; + private int mDXIndex = -1; + private int mDYIndex = -1; + private int mRIndex = -1; - // Top index of stack - private int mTop; + // Top index of stack + private int mTop; - // Constructor parameters - private final float mScale; - private final float mWidth; - private final float mHeight; + // Constructor parameters + private final float mScale; + private final float mWidth; + private final float mHeight; - private void pushIndices() { - mXsIndices.add(mXsIndex); - mYsIndices.add(mYsIndex); - mDXsIndices.add(mDXsIndex); - mDYsIndices.add(mDYsIndex); - mRsIndices.add(mRsIndex); + private void pushIndices() { + mXsIndices.add(mXsIndex); + mYsIndices.add(mYsIndex); + mDXsIndices.add(mDXsIndex); + mDYsIndices.add(mDYsIndex); + mRsIndices.add(mRsIndex); + } + + GlyphContext(float scale, float width, float height) { + mScale = scale; + mWidth = width; + mHeight = height; + + mXsContext.add(mXs); + mYsContext.add(mYs); + mDXsContext.add(mDXs); + mDYsContext.add(mDYs); + mRsContext.add(mRs); + + mXIndices.add(mXIndex); + mYIndices.add(mYIndex); + mDXIndices.add(mDXIndex); + mDYIndices.add(mDYIndex); + mRIndices.add(mRIndex); + + mFontContext.add(topFont); + + pushIndices(); + } + + private void reset() { + mXsIndex = mYsIndex = mDXsIndex = mDYsIndex = mRsIndex = 0; + mXIndex = mYIndex = mDXIndex = mDYIndex = mRIndex = -1; + mX = mY = mDX = mDY = 0; + } + + FontData getFont() { + return topFont; + } + + private FontData getTopOrParentFont(GroupView child) { + if (mTop > 0) { + return topFont; + } else { + GroupView parentRoot = child.getParentTextRoot(); + + while (parentRoot != null) { + FontData map = parentRoot.getGlyphContext().getFont(); + if (map != FontData.Defaults) { + return map; + } + parentRoot = parentRoot.getParentTextRoot(); + } + + return FontData.Defaults; + } + } + + private void pushNodeAndFont(GroupView node, @Nullable ReadableMap font) { + FontData parent = getTopOrParentFont(node); + mTop++; + + if (font == null) { + mFontContext.add(parent); + return; } - GlyphContext(float scale, float width, float height) { - mScale = scale; - mWidth = width; - mHeight = height; + FontData data = new FontData(font, parent, mScale); + mFontSize = data.fontSize; + mFontContext.add(data); + topFont = data; + } - mXsContext.add(mXs); - mYsContext.add(mYs); - mDXsContext.add(mDXs); - mDYsContext.add(mDYs); - mRsContext.add(mRs); + void pushContext(GroupView node, @Nullable ReadableMap font) { + pushNodeAndFont(node, font); + pushIndices(); + } - mXIndices.add(mXIndex); - mYIndices.add(mYIndex); - mDXIndices.add(mDXIndex); - mDYIndices.add(mDYIndex); - mRIndices.add(mRIndex); + private SVGLength[] getStringArrayFromReadableArray(ArrayList readableArray) { + int size = readableArray.size(); + SVGLength[] strings = new SVGLength[size]; + for (int i = 0; i < size; i++) { + strings[i] = readableArray.get(i); + } + return strings; + } - mFontContext.add(topFont); + private double[] getDoubleArrayFromReadableArray(ArrayList readableArray) { + int size = readableArray.size(); + double[] doubles = new double[size]; + for (int i = 0; i < size; i++) { + SVGLength length = readableArray.get(i); + doubles[i] = length.value; + } + return doubles; + } - pushIndices(); + void pushContext( + boolean reset, + TextView node, + @Nullable ReadableMap font, + @Nullable ArrayList x, + @Nullable ArrayList y, + @Nullable ArrayList deltaX, + @Nullable ArrayList deltaY, + @Nullable ArrayList rotate) { + if (reset) { + this.reset(); } - private void reset() { - mXsIndex = mYsIndex = mDXsIndex = mDYsIndex = mRsIndex = 0; - mXIndex = mYIndex = mDXIndex = mDYIndex = mRIndex = -1; - mX = mY = mDX = mDY = 0; + pushNodeAndFont(node, font); + + if (x != null && x.size() != 0) { + mXsIndex++; + mXIndex = -1; + mXIndices.add(mXIndex); + mXs = getStringArrayFromReadableArray(x); + mXsContext.add(mXs); } - FontData getFont() { - return topFont; + if (y != null && y.size() != 0) { + mYsIndex++; + mYIndex = -1; + mYIndices.add(mYIndex); + mYs = getStringArrayFromReadableArray(y); + mYsContext.add(mYs); } - private FontData getTopOrParentFont(GroupView child) { - if (mTop > 0) { - return topFont; - } else { - GroupView parentRoot = child.getParentTextRoot(); - - while (parentRoot != null) { - FontData map = parentRoot.getGlyphContext().getFont(); - if (map != FontData.Defaults) { - return map; - } - parentRoot = parentRoot.getParentTextRoot(); - } - - return FontData.Defaults; - } + if (deltaX != null && deltaX.size() != 0) { + mDXsIndex++; + mDXIndex = -1; + mDXIndices.add(mDXIndex); + mDXs = getStringArrayFromReadableArray(deltaX); + mDXsContext.add(mDXs); } - private void pushNodeAndFont(GroupView node, @Nullable ReadableMap font) { - FontData parent = getTopOrParentFont(node); - mTop++; - - if (font == null) { - mFontContext.add(parent); - return; - } - - FontData data = new FontData(font, parent, mScale); - mFontSize = data.fontSize; - mFontContext.add(data); - topFont = data; - + if (deltaY != null && deltaY.size() != 0) { + mDYsIndex++; + mDYIndex = -1; + mDYIndices.add(mDYIndex); + mDYs = getStringArrayFromReadableArray(deltaY); + mDYsContext.add(mDYs); } - void pushContext(GroupView node, @Nullable ReadableMap font) { - pushNodeAndFont(node, font); - pushIndices(); + if (rotate != null && rotate.size() != 0) { + mRsIndex++; + mRIndex = -1; + mRIndices.add(mRIndex); + mRs = getDoubleArrayFromReadableArray(rotate); + mRsContext.add(mRs); } - private SVGLength[] getStringArrayFromReadableArray(ArrayList readableArray) { - int size = readableArray.size(); - SVGLength[] strings = new SVGLength[size]; - for (int i = 0; i < size; i++) { - strings[i] = readableArray.get(i); - } - return strings; + pushIndices(); + } + + void popContext() { + mFontContext.remove(mTop); + mXsIndices.remove(mTop); + mYsIndices.remove(mTop); + mDXsIndices.remove(mTop); + mDYsIndices.remove(mTop); + mRsIndices.remove(mTop); + + mTop--; + + int x = mXsIndex; + int y = mYsIndex; + int dx = mDXsIndex; + int dy = mDYsIndex; + int r = mRsIndex; + + topFont = mFontContext.get(mTop); + mXsIndex = mXsIndices.get(mTop); + mYsIndex = mYsIndices.get(mTop); + mDXsIndex = mDXsIndices.get(mTop); + mDYsIndex = mDYsIndices.get(mTop); + mRsIndex = mRsIndices.get(mTop); + + if (x != mXsIndex) { + mXsContext.remove(x); + mXs = mXsContext.get(mXsIndex); + mXIndex = mXIndices.get(mXsIndex); + } + if (y != mYsIndex) { + mYsContext.remove(y); + mYs = mYsContext.get(mYsIndex); + mYIndex = mYIndices.get(mYsIndex); + } + if (dx != mDXsIndex) { + mDXsContext.remove(dx); + mDXs = mDXsContext.get(mDXsIndex); + mDXIndex = mDXIndices.get(mDXsIndex); + } + if (dy != mDYsIndex) { + mDYsContext.remove(dy); + mDYs = mDYsContext.get(mDYsIndex); + mDYIndex = mDYIndices.get(mDYsIndex); + } + if (r != mRsIndex) { + mRsContext.remove(r); + mRs = mRsContext.get(mRsIndex); + mRIndex = mRIndices.get(mRsIndex); + } + } + + private static void incrementIndices(ArrayList indices, int topIndex) { + for (int index = topIndex; index >= 0; index--) { + int xIndex = indices.get(index); + indices.set(index, xIndex + 1); + } + } + + // https://www.w3.org/TR/SVG11/text.html#FontSizeProperty + + /** + * Get font size from context. + * + *

‘font-size’ Value: < absolute-size > | < relative-size > | < length > | < percentage > | + * inherit Initial: medium Applies to: text content elements Inherited: yes, the computed value is + * inherited Percentages: refer to parent element's font size Media: visual Animatable: yes + * + *

This property refers to the size of the font from baseline to baseline when multiple lines + * of text are set solid in a multiline layout environment. + * + *

For SVG, if a < length > is provided without a unit identifier (e.g., an unqualified number + * such as 128), the SVG user agent processes the < length > as a height value in the current user + * coordinate system. + * + *

If a < length > is provided with one of the unit identifiers (e.g., 12pt or 10%), then the + * SVG user agent converts the < length > into a corresponding value in the current user + * coordinate system by applying the rules described in Units. + * + *

Except for any additional information provided in this specification, the normative + * definition of the property is in CSS2 ([CSS2], section 15.2.4). + */ + double getFontSize() { + return mFontSize; + } + + double nextX(double advance) { + incrementIndices(mXIndices, mXsIndex); + + int nextIndex = mXIndex + 1; + if (nextIndex < mXs.length) { + mDX = 0; + mXIndex = nextIndex; + SVGLength string = mXs[nextIndex]; + mX = PropHelper.fromRelative(string, mWidth, 0, mScale, mFontSize); } - private double[] getDoubleArrayFromReadableArray(ArrayList readableArray) { - int size = readableArray.size(); - double[] doubles = new double[size]; - for (int i = 0; i < size; i++) { - SVGLength length = readableArray.get(i); - doubles[i] = length.value; - } - return doubles; + mX += advance; + + return mX; + } + + double nextY() { + incrementIndices(mYIndices, mYsIndex); + + int nextIndex = mYIndex + 1; + if (nextIndex < mYs.length) { + mDY = 0; + mYIndex = nextIndex; + SVGLength string = mYs[nextIndex]; + mY = PropHelper.fromRelative(string, mHeight, 0, mScale, mFontSize); } - void pushContext( - boolean reset, - TextView node, - @Nullable ReadableMap font, - @Nullable ArrayList x, - @Nullable ArrayList y, - @Nullable ArrayList deltaX, - @Nullable ArrayList deltaY, - @Nullable ArrayList rotate - ) { - if (reset) { - this.reset(); - } + return mY; + } - pushNodeAndFont(node, font); + double nextDeltaX() { + incrementIndices(mDXIndices, mDXsIndex); - if (x != null && x.size() != 0) { - mXsIndex++; - mXIndex = -1; - mXIndices.add(mXIndex); - mXs = getStringArrayFromReadableArray(x); - mXsContext.add(mXs); - } - - if (y != null && y.size() != 0) { - mYsIndex++; - mYIndex = -1; - mYIndices.add(mYIndex); - mYs = getStringArrayFromReadableArray(y); - mYsContext.add(mYs); - } - - if (deltaX != null && deltaX.size() != 0) { - mDXsIndex++; - mDXIndex = -1; - mDXIndices.add(mDXIndex); - mDXs = getStringArrayFromReadableArray(deltaX); - mDXsContext.add(mDXs); - } - - if (deltaY != null && deltaY.size() != 0) { - mDYsIndex++; - mDYIndex = -1; - mDYIndices.add(mDYIndex); - mDYs = getStringArrayFromReadableArray(deltaY); - mDYsContext.add(mDYs); - } - - if (rotate != null && rotate.size() != 0) { - mRsIndex++; - mRIndex = -1; - mRIndices.add(mRIndex); - mRs = getDoubleArrayFromReadableArray(rotate); - mRsContext.add(mRs); - } - - pushIndices(); + int nextIndex = mDXIndex + 1; + if (nextIndex < mDXs.length) { + mDXIndex = nextIndex; + SVGLength string = mDXs[nextIndex]; + double val = PropHelper.fromRelative(string, mWidth, 0, mScale, mFontSize); + mDX += val; } - void popContext() { - mFontContext.remove(mTop); - mXsIndices.remove(mTop); - mYsIndices.remove(mTop); - mDXsIndices.remove(mTop); - mDYsIndices.remove(mTop); - mRsIndices.remove(mTop); + return mDX; + } - mTop--; + double nextDeltaY() { + incrementIndices(mDYIndices, mDYsIndex); - int x = mXsIndex; - int y = mYsIndex; - int dx = mDXsIndex; - int dy = mDYsIndex; - int r = mRsIndex; - - topFont = mFontContext.get(mTop); - mXsIndex = mXsIndices.get(mTop); - mYsIndex = mYsIndices.get(mTop); - mDXsIndex = mDXsIndices.get(mTop); - mDYsIndex = mDYsIndices.get(mTop); - mRsIndex = mRsIndices.get(mTop); - - if (x != mXsIndex) { - mXsContext.remove(x); - mXs = mXsContext.get(mXsIndex); - mXIndex = mXIndices.get(mXsIndex); - } - if (y != mYsIndex) { - mYsContext.remove(y); - mYs = mYsContext.get(mYsIndex); - mYIndex = mYIndices.get(mYsIndex); - } - if (dx != mDXsIndex) { - mDXsContext.remove(dx); - mDXs = mDXsContext.get(mDXsIndex); - mDXIndex = mDXIndices.get(mDXsIndex); - } - if (dy != mDYsIndex) { - mDYsContext.remove(dy); - mDYs = mDYsContext.get(mDYsIndex); - mDYIndex = mDYIndices.get(mDYsIndex); - } - if (r != mRsIndex) { - mRsContext.remove(r); - mRs = mRsContext.get(mRsIndex); - mRIndex = mRIndices.get(mRsIndex); - } + int nextIndex = mDYIndex + 1; + if (nextIndex < mDYs.length) { + mDYIndex = nextIndex; + SVGLength string = mDYs[nextIndex]; + double val = PropHelper.fromRelative(string, mHeight, 0, mScale, mFontSize); + mDY += val; } - private static void incrementIndices(ArrayList indices, int topIndex) { - for (int index = topIndex; index >= 0; index--) { - int xIndex = indices.get(index); - indices.set(index, xIndex + 1); - } - } + return mDY; + } - // https://www.w3.org/TR/SVG11/text.html#FontSizeProperty + double nextRotation() { + incrementIndices(mRIndices, mRsIndex); - /** - * Get font size from context. - *

- * ‘font-size’ - * Value: < absolute-size > | < relative-size > | < length > | < percentage > | inherit - * Initial: medium - * Applies to: text content elements - * Inherited: yes, the computed value is inherited - * Percentages: refer to parent element's font size - * Media: visual - * Animatable: yes - *

- * This property refers to the size of the font from baseline to - * baseline when multiple lines of text are set solid in a multiline - * layout environment. - *

- * For SVG, if a < length > is provided without a unit identifier - * (e.g., an unqualified number such as 128), the SVG user agent - * processes the < length > as a height value in the current user - * coordinate system. - *

- * If a < length > is provided with one of the unit identifiers - * (e.g., 12pt or 10%), then the SVG user agent converts the - * < length > into a corresponding value in the current user - * coordinate system by applying the rules described in Units. - *

- * Except for any additional information provided in this specification, - * the normative definition of the property is in CSS2 ([CSS2], section 15.2.4). - */ - double getFontSize() { - return mFontSize; - } + mRIndex = Math.min(mRIndex + 1, mRs.length - 1); - double nextX(double advance) { - incrementIndices(mXIndices, mXsIndex); + return mRs[mRIndex]; + } - int nextIndex = mXIndex + 1; - if (nextIndex < mXs.length) { - mDX = 0; - mXIndex = nextIndex; - SVGLength string = mXs[nextIndex]; - mX = PropHelper.fromRelative(string, mWidth, 0, mScale, mFontSize); - } + float getWidth() { + return mWidth; + } - mX += advance; - - return mX; - } - - double nextY() { - incrementIndices(mYIndices, mYsIndex); - - int nextIndex = mYIndex + 1; - if (nextIndex < mYs.length) { - mDY = 0; - mYIndex = nextIndex; - SVGLength string = mYs[nextIndex]; - mY = PropHelper.fromRelative(string, mHeight, 0, mScale, mFontSize); - } - - return mY; - } - - double nextDeltaX() { - incrementIndices(mDXIndices, mDXsIndex); - - int nextIndex = mDXIndex + 1; - if (nextIndex < mDXs.length) { - mDXIndex = nextIndex; - SVGLength string = mDXs[nextIndex]; - double val = PropHelper.fromRelative(string, mWidth, 0, mScale, mFontSize); - mDX += val; - } - - return mDX; - } - - double nextDeltaY() { - incrementIndices(mDYIndices, mDYsIndex); - - int nextIndex = mDYIndex + 1; - if (nextIndex < mDYs.length) { - mDYIndex = nextIndex; - SVGLength string = mDYs[nextIndex]; - double val = PropHelper.fromRelative(string, mHeight, 0, mScale, mFontSize); - mDY += val; - } - - return mDY; - } - - double nextRotation() { - incrementIndices(mRIndices, mRsIndex); - - mRIndex = Math.min(mRIndex + 1, mRs.length - 1); - - return mRs[mRIndex]; - } - - float getWidth() { - return mWidth; - } - - float getHeight() { - return mHeight; - } + float getHeight() { + return mHeight; + } } diff --git a/android/src/main/java/com/horcrux/svg/GlyphPathBag.java b/android/src/main/java/com/horcrux/svg/GlyphPathBag.java index b520c599..1a8dca34 100644 --- a/android/src/main/java/com/horcrux/svg/GlyphPathBag.java +++ b/android/src/main/java/com/horcrux/svg/GlyphPathBag.java @@ -2,47 +2,46 @@ package com.horcrux.svg; import android.graphics.Paint; import android.graphics.Path; - import java.util.ArrayList; class GlyphPathBag { - private final ArrayList paths = new ArrayList<>(); - private final int[][] data = new int[256][]; - private final Paint paint; + private final ArrayList paths = new ArrayList<>(); + private final int[][] data = new int[256][]; + private final Paint paint; - GlyphPathBag(Paint paint) { - this.paint = paint; - // Make indexed-by-one, to allow zero to represent non-cached - paths.add(new Path()); + GlyphPathBag(Paint paint) { + this.paint = paint; + // Make indexed-by-one, to allow zero to represent non-cached + paths.add(new Path()); + } + + Path getOrCreateAndCache(char ch, String current) { + int index = getIndex(ch); + Path cached; + + if (index != 0) { + cached = paths.get(index); + } else { + cached = new Path(); + paint.getTextPath(current, 0, 1, 0, 0, cached); + + int[] bin = data[ch >> 8]; + if (bin == null) { + bin = data[ch >> 8] = new int[256]; + } + bin[ch & 0xFF] = paths.size(); + + paths.add(cached); } - Path getOrCreateAndCache(char ch, String current) { - int index = getIndex(ch); - Path cached; + Path glyph = new Path(); + glyph.addPath(cached); + return glyph; + } - if (index != 0) { - cached = paths.get(index); - } else { - cached = new Path(); - paint.getTextPath(current, 0, 1, 0, 0, cached); - - int[] bin = data[ch >> 8]; - if (bin == null) { - bin = data[ch >> 8] = new int[256]; - } - bin[ch & 0xFF] = paths.size(); - - paths.add(cached); - } - - Path glyph = new Path(); - glyph.addPath(cached); - return glyph; - } - - private int getIndex(char ch) { - int[] bin = data[ch >> 8]; - if (bin == null) return 0; - return bin[ch & 0xFF]; - } + private int getIndex(char ch) { + int[] bin = data[ch >> 8]; + if (bin == null) return 0; + return bin[ch & 0xFF]; + } } diff --git a/android/src/main/java/com/horcrux/svg/GroupView.java b/android/src/main/java/com/horcrux/svg/GroupView.java index e7b54f14..b532631c 100644 --- a/android/src/main/java/com/horcrux/svg/GroupView.java +++ b/android/src/main/java/com/horcrux/svg/GroupView.java @@ -6,7 +6,6 @@ * LICENSE file in the root directory of this source tree. */ - package com.horcrux.svg; import android.annotation.SuppressLint; @@ -19,268 +18,264 @@ import android.graphics.RectF; import android.graphics.Region; import android.os.Build; import android.view.View; - import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.uimanager.annotations.ReactProp; -import com.facebook.react.views.view.ReactViewGroup; - import javax.annotation.Nullable; @SuppressLint("ViewConstructor") class GroupView extends RenderableView { - @Nullable ReadableMap mFont; - private GlyphContext mGlyphContext; + @Nullable ReadableMap mFont; + private GlyphContext mGlyphContext; - public GroupView(ReactContext reactContext) { - super(reactContext); + public GroupView(ReactContext reactContext) { + super(reactContext); + } + + @ReactProp(name = "font") + public void setFont(@Nullable ReadableMap font) { + mFont = font; + invalidate(); + } + + void setupGlyphContext(Canvas canvas) { + RectF clipBounds = new RectF(canvas.getClipBounds()); + if (mMatrix != null) { + mMatrix.mapRect(clipBounds); } - - @ReactProp(name = "font") - public void setFont(@Nullable ReadableMap font) { - mFont = font; - invalidate(); + if (mTransform != null) { + mTransform.mapRect(clipBounds); } + mGlyphContext = new GlyphContext(mScale, clipBounds.width(), clipBounds.height()); + } - void setupGlyphContext(Canvas canvas) { - RectF clipBounds = new RectF(canvas.getClipBounds()); - if (mMatrix != null) { - mMatrix.mapRect(clipBounds); + GlyphContext getGlyphContext() { + return mGlyphContext; + } + + private static T requireNonNull(T obj) { + if (obj == null) throw new NullPointerException(); + return obj; + } + + GlyphContext getTextRootGlyphContext() { + return requireNonNull(getTextRoot()).getGlyphContext(); + } + + void pushGlyphContext() { + getTextRootGlyphContext().pushContext(this, mFont); + } + + void popGlyphContext() { + getTextRootGlyphContext().popContext(); + } + + void draw(final Canvas canvas, final Paint paint, final float opacity) { + setupGlyphContext(canvas); + clip(canvas, paint); + drawGroup(canvas, paint, opacity); + } + + void drawGroup(final Canvas canvas, final Paint paint, final float opacity) { + pushGlyphContext(); + final SvgView svg = getSvgView(); + final GroupView self = this; + final RectF groupRect = new RectF(); + for (int i = 0; i < getChildCount(); i++) { + View child = getChildAt(i); + if (child instanceof MaskView) { + continue; + } + if (child instanceof VirtualView) { + VirtualView node = ((VirtualView) child); + if ("none".equals(node.mDisplay)) { + continue; } - if (mTransform != null) { - mTransform.mapRect(clipBounds); - } - mGlyphContext = new GlyphContext(mScale, clipBounds.width(), clipBounds.height()); - } - - GlyphContext getGlyphContext() { - return mGlyphContext; - } - - private static T requireNonNull(T obj) { - if (obj == null) - throw new NullPointerException(); - return obj; - } - - GlyphContext getTextRootGlyphContext() { - return requireNonNull(getTextRoot()).getGlyphContext(); - } - - void pushGlyphContext() { - getTextRootGlyphContext().pushContext(this, mFont); - } - - void popGlyphContext() { - getTextRootGlyphContext().popContext(); - } - - void draw(final Canvas canvas, final Paint paint, final float opacity) { - setupGlyphContext(canvas); - clip(canvas, paint); - drawGroup(canvas, paint, opacity); - } - - void drawGroup(final Canvas canvas, final Paint paint, final float opacity) { - pushGlyphContext(); - final SvgView svg = getSvgView(); - final GroupView self = this; - final RectF groupRect = new RectF(); - for (int i = 0; i < getChildCount(); i++) { - View child = getChildAt(i); - if (child instanceof MaskView) { - continue; - } - if (child instanceof VirtualView) { - VirtualView node = ((VirtualView)child); - if ("none".equals(node.mDisplay)) { - continue; - } - if (node instanceof RenderableView) { - ((RenderableView)node).mergeProperties(self); - } - - int count = node.saveAndSetupCanvas(canvas, mCTM); - node.render(canvas, paint, opacity * mOpacity); - RectF r = node.getClientRect(); - if (r != null) { - groupRect.union(r); - } - - node.restoreCanvas(canvas, count); - - if (node instanceof RenderableView) { - ((RenderableView)node).resetProperties(); - } - - if (node.isResponsible()) { - svg.enableTouchEvents(); - } - } else if (child instanceof SvgView) { - SvgView svgView = (SvgView)child; - svgView.drawChildren(canvas); - if (svgView.isResponsible()) { - svg.enableTouchEvents(); - } - } - } - this.setClientRect(groupRect); - popGlyphContext(); - } - - void drawPath(Canvas canvas, Paint paint, float opacity) { - super.draw(canvas, paint, opacity); - } - - @Override - Path getPath(final Canvas canvas, final Paint paint) { - if (mPath != null) { - return mPath; - } - mPath = new Path(); - - for (int i = 0; i < getChildCount(); i++) { - View node = getChildAt(i); - if (node instanceof MaskView) { - continue; - } - if (node instanceof VirtualView) { - VirtualView n = (VirtualView)node; - Matrix transform = n.mMatrix; - mPath.addPath(n.getPath(canvas, paint), transform); - } + if (node instanceof RenderableView) { + ((RenderableView) node).mergeProperties(self); } - return mPath; + int count = node.saveAndSetupCanvas(canvas, mCTM); + node.render(canvas, paint, opacity * mOpacity); + RectF r = node.getClientRect(); + if (r != null) { + groupRect.union(r); + } + + node.restoreCanvas(canvas, count); + + if (node instanceof RenderableView) { + ((RenderableView) node).resetProperties(); + } + + if (node.isResponsible()) { + svg.enableTouchEvents(); + } + } else if (child instanceof SvgView) { + SvgView svgView = (SvgView) child; + svgView.drawChildren(canvas); + if (svgView.isResponsible()) { + svg.enableTouchEvents(); + } + } + } + this.setClientRect(groupRect); + popGlyphContext(); + } + + void drawPath(Canvas canvas, Paint paint, float opacity) { + super.draw(canvas, paint, opacity); + } + + @Override + Path getPath(final Canvas canvas, final Paint paint) { + if (mPath != null) { + return mPath; + } + mPath = new Path(); + + for (int i = 0; i < getChildCount(); i++) { + View node = getChildAt(i); + if (node instanceof MaskView) { + continue; + } + if (node instanceof VirtualView) { + VirtualView n = (VirtualView) node; + Matrix transform = n.mMatrix; + mPath.addPath(n.getPath(canvas, paint), transform); + } } - Path getPath(final Canvas canvas, final Paint paint, final Region.Op op) { - final Path path = new Path(); + return mPath; + } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - final Path.Op pop = Path.Op.valueOf(op.name()); - for (int i = 0; i < getChildCount(); i++) { - View node = getChildAt(i); - if (node instanceof MaskView) { - continue; - } - if (node instanceof VirtualView) { - VirtualView n = (VirtualView)node; - Matrix transform = n.mMatrix; - Path p2; - if (n instanceof GroupView) { - p2 = ((GroupView)n).getPath(canvas, paint, op); - } else { - p2 = n.getPath(canvas, paint); - } - p2.transform(transform); - path.op(p2, pop); - } - } - } else { - Rect clipBounds = canvas.getClipBounds(); - final Region bounds = new Region(clipBounds); - final Region r = new Region(); - for (int i = 0; i < getChildCount(); i++) { - View node = getChildAt(i); - if (node instanceof MaskView) { - continue; - } - if (node instanceof VirtualView) { - VirtualView n = (VirtualView)node; - Matrix transform = n.mMatrix; - Path p2; - if (n instanceof GroupView) { - p2 = ((GroupView)n).getPath(canvas, paint, op); - } else { - p2 = n.getPath(canvas, paint); - } - if (transform != null) { - p2.transform(transform); - } - Region r2 = new Region(); - r2.setPath(p2, bounds); - r.op(r2, op); - } - } - path.addPath(r.getBoundaryPath()); + Path getPath(final Canvas canvas, final Paint paint, final Region.Op op) { + final Path path = new Path(); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + final Path.Op pop = Path.Op.valueOf(op.name()); + for (int i = 0; i < getChildCount(); i++) { + View node = getChildAt(i); + if (node instanceof MaskView) { + continue; } - - return path; + if (node instanceof VirtualView) { + VirtualView n = (VirtualView) node; + Matrix transform = n.mMatrix; + Path p2; + if (n instanceof GroupView) { + p2 = ((GroupView) n).getPath(canvas, paint, op); + } else { + p2 = n.getPath(canvas, paint); + } + p2.transform(transform); + path.op(p2, pop); + } + } + } else { + Rect clipBounds = canvas.getClipBounds(); + final Region bounds = new Region(clipBounds); + final Region r = new Region(); + for (int i = 0; i < getChildCount(); i++) { + View node = getChildAt(i); + if (node instanceof MaskView) { + continue; + } + if (node instanceof VirtualView) { + VirtualView n = (VirtualView) node; + Matrix transform = n.mMatrix; + Path p2; + if (n instanceof GroupView) { + p2 = ((GroupView) n).getPath(canvas, paint, op); + } else { + p2 = n.getPath(canvas, paint); + } + if (transform != null) { + p2.transform(transform); + } + Region r2 = new Region(); + r2.setPath(p2, bounds); + r.op(r2, op); + } + } + path.addPath(r.getBoundaryPath()); } - @Override - int hitTest(final float[] src) { - if (!mInvertible || !mTransformInvertible) { - return -1; - } + return path; + } - float[] dst = new float[2]; - mInvMatrix.mapPoints(dst, src); - mInvTransform.mapPoints(dst); + @Override + int hitTest(final float[] src) { + if (!mInvertible || !mTransformInvertible) { + return -1; + } - int x = Math.round(dst[0]); - int y = Math.round(dst[1]); + float[] dst = new float[2]; + mInvMatrix.mapPoints(dst, src); + mInvTransform.mapPoints(dst); - Path clipPath = getClipPath(); - if (clipPath != null) { - if (mClipRegionPath != clipPath) { - mClipRegionPath = clipPath; - mClipBounds = new RectF(); - clipPath.computeBounds(mClipBounds, true); - mClipRegion = getRegion(clipPath, mClipBounds); - } - if (!mClipRegion.contains(x, y)) { - return -1; - } - } - - for (int i = getChildCount() - 1; i >= 0; i--) { - View child = getChildAt(i); - if (child instanceof VirtualView) { - if (child instanceof MaskView) { - continue; - } - - VirtualView node = (VirtualView) child; - - int hitChild = node.hitTest(dst); - if (hitChild != -1) { - return (node.isResponsible() || hitChild != child.getId()) ? hitChild : getId(); - } - } else if (child instanceof SvgView) { - SvgView node = (SvgView) child; - - int hitChild = node.reactTagForTouch(dst[0], dst[1]); - if (hitChild != child.getId()) { - return hitChild; - } - } - } + int x = Math.round(dst[0]); + int y = Math.round(dst[1]); + Path clipPath = getClipPath(); + if (clipPath != null) { + if (mClipRegionPath != clipPath) { + mClipRegionPath = clipPath; + mClipBounds = new RectF(); + clipPath.computeBounds(mClipBounds, true); + mClipRegion = getRegion(clipPath, mClipBounds); + } + if (!mClipRegion.contains(x, y)) { return -1; + } } - void saveDefinition() { - if (mName != null) { - getSvgView().defineTemplate(this, mName); + for (int i = getChildCount() - 1; i >= 0; i--) { + View child = getChildAt(i); + if (child instanceof VirtualView) { + if (child instanceof MaskView) { + continue; } - for (int i = 0; i < getChildCount(); i++) { - View node = getChildAt(i); - if (node instanceof VirtualView) { - ((VirtualView)node).saveDefinition(); - } + VirtualView node = (VirtualView) child; + + int hitChild = node.hitTest(dst); + if (hitChild != -1) { + return (node.isResponsible() || hitChild != child.getId()) ? hitChild : getId(); } + } else if (child instanceof SvgView) { + SvgView node = (SvgView) child; + + int hitChild = node.reactTagForTouch(dst[0], dst[1]); + if (hitChild != child.getId()) { + return hitChild; + } + } } - @Override - void resetProperties() { - for (int i = 0; i < getChildCount(); i++) { - View node = getChildAt(i); - if (node instanceof RenderableView) { - ((RenderableView)node).resetProperties(); - } - } + return -1; + } + + void saveDefinition() { + if (mName != null) { + getSvgView().defineTemplate(this, mName); } + + for (int i = 0; i < getChildCount(); i++) { + View node = getChildAt(i); + if (node instanceof VirtualView) { + ((VirtualView) node).saveDefinition(); + } + } + } + + @Override + void resetProperties() { + for (int i = 0; i < getChildCount(); i++) { + View node = getChildAt(i); + if (node instanceof RenderableView) { + ((RenderableView) node).resetProperties(); + } + } + } } diff --git a/android/src/main/java/com/horcrux/svg/ImageView.java b/android/src/main/java/com/horcrux/svg/ImageView.java index ab049006..4ddd2b9b 100644 --- a/android/src/main/java/com/horcrux/svg/ImageView.java +++ b/android/src/main/java/com/horcrux/svg/ImageView.java @@ -6,7 +6,6 @@ * LICENSE file in the root directory of this source tree. */ - package com.horcrux.svg; import android.annotation.SuppressLint; @@ -17,7 +16,6 @@ import android.graphics.Paint; import android.graphics.Path; import android.graphics.RectF; import android.net.Uri; - import com.facebook.common.executors.UiThreadImmediateExecutorService; import com.facebook.common.logging.FLog; import com.facebook.common.references.CloseableReference; @@ -35,235 +33,240 @@ import com.facebook.react.common.ReactConstants; import com.facebook.react.uimanager.annotations.ReactProp; import com.facebook.react.views.imagehelper.ImageSource; import com.facebook.react.views.imagehelper.ResourceDrawableIdHelper; - import java.util.concurrent.atomic.AtomicBoolean; - import javax.annotation.Nonnull; import javax.annotation.Nullable; @SuppressLint("ViewConstructor") class ImageView extends RenderableView { - private SVGLength mX; - private SVGLength mY; - private SVGLength mW; - private SVGLength mH; - private String uriString; - private int mImageWidth; - private int mImageHeight; - private String mAlign; - private int mMeetOrSlice; - private final AtomicBoolean mLoading = new AtomicBoolean(false); + private SVGLength mX; + private SVGLength mY; + private SVGLength mW; + private SVGLength mH; + private String uriString; + private int mImageWidth; + private int mImageHeight; + private String mAlign; + private int mMeetOrSlice; + private final AtomicBoolean mLoading = new AtomicBoolean(false); - public ImageView(ReactContext reactContext) { - super(reactContext); - } + public ImageView(ReactContext reactContext) { + super(reactContext); + } - @ReactProp(name = "x") - public void setX(Dynamic x) { - mX = SVGLength.from(x); - invalidate(); - } + @ReactProp(name = "x") + public void setX(Dynamic x) { + mX = SVGLength.from(x); + invalidate(); + } public void setX(String x) { mX = SVGLength.from(x); invalidate(); } - @ReactProp(name = "y") - public void setY(Dynamic y) { - mY = SVGLength.from(y); - invalidate(); - } + @ReactProp(name = "y") + public void setY(Dynamic y) { + mY = SVGLength.from(y); + invalidate(); + } public void setY(String y) { mY = SVGLength.from(y); invalidate(); } - @ReactProp(name = "width") - public void setWidth(Dynamic width) { - mW = SVGLength.from(width); - invalidate(); - } + @ReactProp(name = "width") + public void setWidth(Dynamic width) { + mW = SVGLength.from(width); + invalidate(); + } public void setWidth(String width) { mW = SVGLength.from(width); invalidate(); } - @ReactProp(name = "height") - public void setHeight(Dynamic height) { - mH = SVGLength.from(height); - invalidate(); - } + @ReactProp(name = "height") + public void setHeight(Dynamic height) { + mH = SVGLength.from(height); + invalidate(); + } public void setHeight(String height) { mH = SVGLength.from(height); invalidate(); } + @ReactProp(name = "src") + public void setSrc(@Nullable ReadableMap src) { + if (src != null) { + uriString = src.getString("uri"); - @ReactProp(name = "src") - public void setSrc(@Nullable ReadableMap src) { - if (src != null) { - uriString = src.getString("uri"); + if (uriString == null || uriString.isEmpty()) { + // TODO: give warning about this + return; + } - if (uriString == null || uriString.isEmpty()) { - //TODO: give warning about this - return; - } - - if (src.hasKey("width") && src.hasKey("height")) { - mImageWidth = src.getInt("width"); - mImageHeight = src.getInt("height"); - } else { - mImageWidth = 0; - mImageHeight = 0; - } - Uri mUri = Uri.parse(uriString); - if (mUri.getScheme() == null) { - ResourceDrawableIdHelper.getInstance().getResourceDrawableUri(mContext, uriString); - } - } + if (src.hasKey("width") && src.hasKey("height")) { + mImageWidth = src.getInt("width"); + mImageHeight = src.getInt("height"); + } else { + mImageWidth = 0; + mImageHeight = 0; + } + Uri mUri = Uri.parse(uriString); + if (mUri.getScheme() == null) { + ResourceDrawableIdHelper.getInstance().getResourceDrawableUri(mContext, uriString); + } } + } - @ReactProp(name = "align") - public void setAlign(String align) { - mAlign = align; - invalidate(); + @ReactProp(name = "align") + public void setAlign(String align) { + mAlign = align; + invalidate(); + } + + @ReactProp(name = "meetOrSlice") + public void setMeetOrSlice(int meetOrSlice) { + mMeetOrSlice = meetOrSlice; + invalidate(); + } + + @Override + void draw(final Canvas canvas, final Paint paint, final float opacity) { + if (!mLoading.get()) { + ImagePipeline imagePipeline = Fresco.getImagePipeline(); + ImageSource imageSource = new ImageSource(mContext, uriString); + ImageRequest request = ImageRequest.fromUri(imageSource.getUri()); + boolean inMemoryCache = imagePipeline.isInBitmapMemoryCache(request); + + if (inMemoryCache) { + tryRenderFromBitmapCache(imagePipeline, request, canvas, paint, opacity * mOpacity); + } else { + loadBitmap(imagePipeline, request); + } } + } - @ReactProp(name = "meetOrSlice") - public void setMeetOrSlice(int meetOrSlice) { - mMeetOrSlice = meetOrSlice; - invalidate(); - } + @Override + Path getPath(Canvas canvas, Paint paint) { + mPath = new Path(); + mPath.addRect(getRect(), Path.Direction.CW); + return mPath; + } - @Override - void draw(final Canvas canvas, final Paint paint, final float opacity) { - if (!mLoading.get()) { - ImagePipeline imagePipeline = Fresco.getImagePipeline(); - ImageSource imageSource = new ImageSource(mContext, uriString); - ImageRequest request = ImageRequest.fromUri(imageSource.getUri()); - boolean inMemoryCache = imagePipeline.isInBitmapMemoryCache(request); - - if (inMemoryCache) { - tryRenderFromBitmapCache(imagePipeline, request, canvas, paint, opacity * mOpacity); - } else { - loadBitmap(imagePipeline, request); + private void loadBitmap(final ImagePipeline imagePipeline, final ImageRequest request) { + mLoading.set(true); + final DataSource> dataSource = + imagePipeline.fetchDecodedImage(request, mContext); + BaseBitmapDataSubscriber subscriber = + new BaseBitmapDataSubscriber() { + @Override + public void onNewResultImpl(Bitmap bitmap) { + mLoading.set(false); + SvgView view = getSvgView(); + if (view != null) { + view.invalidate(); } - } - } + } - @Override - Path getPath(Canvas canvas, Paint paint) { - mPath = new Path(); - mPath.addRect(getRect(), Path.Direction.CW); - return mPath; - } - - private void loadBitmap(final ImagePipeline imagePipeline, final ImageRequest request) { - mLoading.set(true); - final DataSource> dataSource - = imagePipeline.fetchDecodedImage(request, mContext); - BaseBitmapDataSubscriber subscriber = new BaseBitmapDataSubscriber() { - @Override - public void onNewResultImpl(Bitmap bitmap) { - mLoading.set(false); - SvgView view = getSvgView(); - if (view != null) { - view.invalidate(); - } - } - - @Override - public void onFailureImpl(DataSource dataSource) { - // No cleanup required here. - // TODO: more details about this failure - mLoading.set(false); - FLog.w(ReactConstants.TAG, dataSource.getFailureCause(), "RNSVG: fetchDecodedImage failed!"); - } + @Override + public void onFailureImpl(DataSource dataSource) { + // No cleanup required here. + // TODO: more details about this failure + mLoading.set(false); + FLog.w( + ReactConstants.TAG, + dataSource.getFailureCause(), + "RNSVG: fetchDecodedImage failed!"); + } }; - dataSource.subscribe(subscriber, UiThreadImmediateExecutorService.getInstance()); + dataSource.subscribe(subscriber, UiThreadImmediateExecutorService.getInstance()); + } + + @Nonnull + private RectF getRect() { + double x = relativeOnWidth(mX); + double y = relativeOnHeight(mY); + double w = relativeOnWidth(mW); + double h = relativeOnHeight(mH); + if (w == 0) { + w = mImageWidth * mScale; + } + if (h == 0) { + h = mImageHeight * mScale; } - @Nonnull - private RectF getRect() { - double x = relativeOnWidth(mX); - double y = relativeOnHeight(mY); - double w = relativeOnWidth(mW); - double h = relativeOnHeight(mH); - if (w == 0) { - w = mImageWidth * mScale; - } - if (h == 0) { - h = mImageHeight * mScale; - } + return new RectF((float) x, (float) y, (float) (x + w), (float) (y + h)); + } - return new RectF((float) x, (float) y, (float) (x + w), (float) (y + h)); + private void doRender(Canvas canvas, Paint paint, Bitmap bitmap, float opacity) { + if (mImageWidth == 0 || mImageHeight == 0) { + mImageWidth = bitmap.getWidth(); + mImageHeight = bitmap.getHeight(); } - private void doRender(Canvas canvas, Paint paint, Bitmap bitmap, float opacity) { - if (mImageWidth == 0 || mImageHeight == 0) { - mImageWidth = bitmap.getWidth(); - mImageHeight = bitmap.getHeight(); - } + RectF renderRect = getRect(); + RectF vbRect = new RectF(0, 0, mImageWidth, mImageHeight); + Matrix transform = ViewBox.getTransform(vbRect, renderRect, mAlign, mMeetOrSlice); + transform.mapRect(vbRect); - RectF renderRect = getRect(); - RectF vbRect = new RectF(0, 0, mImageWidth, mImageHeight); - Matrix transform = ViewBox.getTransform(vbRect, renderRect, mAlign, mMeetOrSlice); - transform.mapRect(vbRect); + canvas.clipPath(getPath(canvas, paint)); - canvas.clipPath(getPath(canvas, paint)); - - Path clipPath = getClipPath(canvas, paint); - if (clipPath != null) { - canvas.clipPath(clipPath); - } - - Paint alphaPaint = new Paint(); - alphaPaint.setAlpha((int) (opacity * 255)); - canvas.drawBitmap(bitmap, null, vbRect, alphaPaint); - mCTM.mapRect(vbRect); - this.setClientRect(vbRect); + Path clipPath = getClipPath(canvas, paint); + if (clipPath != null) { + canvas.clipPath(clipPath); } - private void tryRenderFromBitmapCache(ImagePipeline imagePipeline, ImageRequest request, Canvas canvas, Paint paint, float opacity) { - final DataSource> dataSource - = imagePipeline.fetchImageFromBitmapCache(request, mContext); + Paint alphaPaint = new Paint(); + alphaPaint.setAlpha((int) (opacity * 255)); + canvas.drawBitmap(bitmap, null, vbRect, alphaPaint); + mCTM.mapRect(vbRect); + this.setClientRect(vbRect); + } - try { - final CloseableReference imageReference = dataSource.getResult(); - if (imageReference == null) { - return; - } + private void tryRenderFromBitmapCache( + ImagePipeline imagePipeline, + ImageRequest request, + Canvas canvas, + Paint paint, + float opacity) { + final DataSource> dataSource = + imagePipeline.fetchImageFromBitmapCache(request, mContext); - try { - CloseableImage closeableImage = imageReference.get(); - if (!(closeableImage instanceof CloseableBitmap)) { - return; - } + try { + final CloseableReference imageReference = dataSource.getResult(); + if (imageReference == null) { + return; + } - CloseableBitmap closeableBitmap = (CloseableBitmap) closeableImage; - final Bitmap bitmap = closeableBitmap.getUnderlyingBitmap(); - - if (bitmap == null) { - return; - } - - doRender(canvas, paint, bitmap, opacity); - - } catch (Exception e) { - throw new IllegalStateException(e); - } finally { - CloseableReference.closeSafely(imageReference); - } - - } catch (Exception e) { - throw new IllegalStateException(e); - } finally { - dataSource.close(); + try { + CloseableImage closeableImage = imageReference.get(); + if (!(closeableImage instanceof CloseableBitmap)) { + return; } - } + CloseableBitmap closeableBitmap = (CloseableBitmap) closeableImage; + final Bitmap bitmap = closeableBitmap.getUnderlyingBitmap(); + + if (bitmap == null) { + return; + } + + doRender(canvas, paint, bitmap, opacity); + + } catch (Exception e) { + throw new IllegalStateException(e); + } finally { + CloseableReference.closeSafely(imageReference); + } + + } catch (Exception e) { + throw new IllegalStateException(e); + } finally { + dataSource.close(); + } + } } diff --git a/android/src/main/java/com/horcrux/svg/LineView.java b/android/src/main/java/com/horcrux/svg/LineView.java index a5eb5229..0ab3e65f 100644 --- a/android/src/main/java/com/horcrux/svg/LineView.java +++ b/android/src/main/java/com/horcrux/svg/LineView.java @@ -6,83 +6,81 @@ * LICENSE file in the root directory of this source tree. */ - package com.horcrux.svg; import android.annotation.SuppressLint; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; - import com.facebook.react.bridge.Dynamic; import com.facebook.react.bridge.ReactContext; import com.facebook.react.uimanager.annotations.ReactProp; @SuppressLint("ViewConstructor") class LineView extends RenderableView { - private SVGLength mX1; - private SVGLength mY1; - private SVGLength mX2; - private SVGLength mY2; + private SVGLength mX1; + private SVGLength mY1; + private SVGLength mX2; + private SVGLength mY2; - public LineView(ReactContext reactContext) { - super(reactContext); - } + public LineView(ReactContext reactContext) { + super(reactContext); + } - @ReactProp(name = "x1") - public void setX1(Dynamic x1) { - mX1 = SVGLength.from(x1); - invalidate(); - } + @ReactProp(name = "x1") + public void setX1(Dynamic x1) { + mX1 = SVGLength.from(x1); + invalidate(); + } public void setX1(String x1) { mX1 = SVGLength.from(x1); invalidate(); } - @ReactProp(name = "y1") - public void setY1(Dynamic y1) { - mY1 = SVGLength.from(y1); - invalidate(); - } + @ReactProp(name = "y1") + public void setY1(Dynamic y1) { + mY1 = SVGLength.from(y1); + invalidate(); + } public void setY1(String y1) { mY1 = SVGLength.from(y1); invalidate(); } - @ReactProp(name = "x2") - public void setX2(Dynamic x2) { - mX2 = SVGLength.from(x2); - invalidate(); - } + @ReactProp(name = "x2") + public void setX2(Dynamic x2) { + mX2 = SVGLength.from(x2); + invalidate(); + } public void setX2(String x2) { mX2 = SVGLength.from(x2); invalidate(); } - @ReactProp(name = "y2") - public void setY2(Dynamic y2) { - mY2 = SVGLength.from(y2); - invalidate(); - } + @ReactProp(name = "y2") + public void setY2(Dynamic y2) { + mY2 = SVGLength.from(y2); + invalidate(); + } public void setY2(String y2) { mY2 = SVGLength.from(y2); invalidate(); } - @Override - Path getPath(Canvas canvas, Paint paint) { - Path path = new Path(); - double x1 = relativeOnWidth(mX1); - double y1 = relativeOnHeight(mY1); - double x2 = relativeOnWidth(mX2); - double y2 = relativeOnHeight(mY2); + @Override + Path getPath(Canvas canvas, Paint paint) { + Path path = new Path(); + double x1 = relativeOnWidth(mX1); + double y1 = relativeOnHeight(mY1); + double x2 = relativeOnWidth(mX2); + double y2 = relativeOnHeight(mY2); - path.moveTo((float) x1, (float) y1); - path.lineTo((float) x2, (float) y2); - return path; - } + path.moveTo((float) x1, (float) y1); + path.lineTo((float) x2, (float) y2); + return path; + } } diff --git a/android/src/main/java/com/horcrux/svg/LinearGradientView.java b/android/src/main/java/com/horcrux/svg/LinearGradientView.java index b2c432be..43b9d1a5 100644 --- a/android/src/main/java/com/horcrux/svg/LinearGradientView.java +++ b/android/src/main/java/com/horcrux/svg/LinearGradientView.java @@ -6,140 +6,138 @@ * LICENSE file in the root directory of this source tree. */ - package com.horcrux.svg; import android.annotation.SuppressLint; import android.graphics.Matrix; - import com.facebook.common.logging.FLog; import com.facebook.react.bridge.Dynamic; import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.common.ReactConstants; import com.facebook.react.uimanager.annotations.ReactProp; - import javax.annotation.Nullable; @SuppressLint("ViewConstructor") class LinearGradientView extends DefinitionView { - private SVGLength mX1; - private SVGLength mY1; - private SVGLength mX2; - private SVGLength mY2; - private ReadableArray mGradient; - private Brush.BrushUnits mGradientUnits; + private SVGLength mX1; + private SVGLength mY1; + private SVGLength mX2; + private SVGLength mY2; + private ReadableArray mGradient; + private Brush.BrushUnits mGradientUnits; - private static final float[] sRawMatrix = new float[]{ + private static final float[] sRawMatrix = + new float[] { 1, 0, 0, 0, 1, 0, 0, 0, 1 - }; - private Matrix mMatrix = null; + }; + private Matrix mMatrix = null; - public LinearGradientView(ReactContext reactContext) { - super(reactContext); - } + public LinearGradientView(ReactContext reactContext) { + super(reactContext); + } - @ReactProp(name = "x1") - public void setX1(Dynamic x1) { - mX1 = SVGLength.from(x1); - invalidate(); - } + @ReactProp(name = "x1") + public void setX1(Dynamic x1) { + mX1 = SVGLength.from(x1); + invalidate(); + } public void setX1(String x1) { mX1 = SVGLength.from(x1); invalidate(); } - @ReactProp(name = "y1") - public void setY1(Dynamic y1) { - mY1 = SVGLength.from(y1); - invalidate(); - } + @ReactProp(name = "y1") + public void setY1(Dynamic y1) { + mY1 = SVGLength.from(y1); + invalidate(); + } public void setY1(String y1) { mY1 = SVGLength.from(y1); invalidate(); } - @ReactProp(name = "x2") - public void setX2(Dynamic x2) { - mX2 = SVGLength.from(x2); - invalidate(); - } + @ReactProp(name = "x2") + public void setX2(Dynamic x2) { + mX2 = SVGLength.from(x2); + invalidate(); + } public void setX2(String x2) { mX2 = SVGLength.from(x2); invalidate(); } - @ReactProp(name = "y2") - public void setY2(Dynamic y2) { - mY2 = SVGLength.from(y2); - invalidate(); - } + @ReactProp(name = "y2") + public void setY2(Dynamic y2) { + mY2 = SVGLength.from(y2); + invalidate(); + } public void setY2(String y2) { mY2 = SVGLength.from(y2); invalidate(); } - @ReactProp(name = "gradient") - public void setGradient(ReadableArray gradient) { - mGradient = gradient; - invalidate(); - } + @ReactProp(name = "gradient") + public void setGradient(ReadableArray gradient) { + mGradient = gradient; + invalidate(); + } - @ReactProp(name = "gradientUnits") - public void setGradientUnits(int gradientUnits) { - switch (gradientUnits) { - case 0: - mGradientUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX; - break; - case 1: - mGradientUnits = Brush.BrushUnits.USER_SPACE_ON_USE; - break; + @ReactProp(name = "gradientUnits") + public void setGradientUnits(int gradientUnits) { + switch (gradientUnits) { + case 0: + mGradientUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX; + break; + case 1: + mGradientUnits = Brush.BrushUnits.USER_SPACE_ON_USE; + break; + } + invalidate(); + } + + @ReactProp(name = "gradientTransform") + public void setGradientTransform(@Nullable ReadableArray matrixArray) { + if (matrixArray != null) { + int matrixSize = PropHelper.toMatrixData(matrixArray, sRawMatrix, mScale); + if (matrixSize == 6) { + if (mMatrix == null) { + mMatrix = new Matrix(); } - invalidate(); + mMatrix.setValues(sRawMatrix); + } else if (matrixSize != -1) { + FLog.w(ReactConstants.TAG, "RNSVG: Transform matrices must be of size 6"); + } + } else { + mMatrix = null; } - @ReactProp(name = "gradientTransform") - public void setGradientTransform(@Nullable ReadableArray matrixArray) { - if (matrixArray != null) { - int matrixSize = PropHelper.toMatrixData(matrixArray, sRawMatrix, mScale); - if (matrixSize == 6) { - if (mMatrix == null) { - mMatrix = new Matrix(); - } - mMatrix.setValues(sRawMatrix); - } else if (matrixSize != -1) { - FLog.w(ReactConstants.TAG, "RNSVG: Transform matrices must be of size 6"); - } - } else { - mMatrix = null; - } + invalidate(); + } - invalidate(); - } - - @Override - void saveDefinition() { - if (mName != null) { - SVGLength[] points = new SVGLength[]{mX1, mY1, mX2, mY2}; - Brush brush = new Brush(Brush.BrushType.LINEAR_GRADIENT, points, mGradientUnits); - brush.setGradientColors(mGradient); - if (mMatrix != null) { - brush.setGradientTransform(mMatrix); - } - - SvgView svg = getSvgView(); - if (mGradientUnits == Brush.BrushUnits.USER_SPACE_ON_USE) { - brush.setUserSpaceBoundingBox(svg.getCanvasBounds()); - } - - svg.defineBrush(brush, mName); - } + @Override + void saveDefinition() { + if (mName != null) { + SVGLength[] points = new SVGLength[] {mX1, mY1, mX2, mY2}; + Brush brush = new Brush(Brush.BrushType.LINEAR_GRADIENT, points, mGradientUnits); + brush.setGradientColors(mGradient); + if (mMatrix != null) { + brush.setGradientTransform(mMatrix); + } + + SvgView svg = getSvgView(); + if (mGradientUnits == Brush.BrushUnits.USER_SPACE_ON_USE) { + brush.setUserSpaceBoundingBox(svg.getCanvasBounds()); + } + + svg.defineBrush(brush, mName); } + } } diff --git a/android/src/main/java/com/horcrux/svg/MarkerView.java b/android/src/main/java/com/horcrux/svg/MarkerView.java index cdcc41f7..86d0cb61 100644 --- a/android/src/main/java/com/horcrux/svg/MarkerView.java +++ b/android/src/main/java/com/horcrux/svg/MarkerView.java @@ -6,7 +6,6 @@ * LICENSE file in the root directory of this source tree. */ - package com.horcrux.svg; import android.annotation.SuppressLint; @@ -15,7 +14,6 @@ import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.RectF; import android.view.View; - import com.facebook.react.bridge.Dynamic; import com.facebook.react.bridge.ReactContext; import com.facebook.react.uimanager.annotations.ReactProp; @@ -23,167 +21,173 @@ import com.facebook.react.uimanager.annotations.ReactProp; @SuppressLint("ViewConstructor") class MarkerView extends GroupView { - private SVGLength mRefX; - private SVGLength mRefY; - private SVGLength mMarkerWidth; - private SVGLength mMarkerHeight; - private String mMarkerUnits; - private String mOrient; + private SVGLength mRefX; + private SVGLength mRefY; + private SVGLength mMarkerWidth; + private SVGLength mMarkerHeight; + private String mMarkerUnits; + private String mOrient; - private float mMinX; - private float mMinY; - private float mVbWidth; - private float mVbHeight; - String mAlign; - int mMeetOrSlice; + private float mMinX; + private float mMinY; + private float mVbWidth; + private float mVbHeight; + String mAlign; + int mMeetOrSlice; - Matrix markerTransform = new Matrix(); + Matrix markerTransform = new Matrix(); - public MarkerView(ReactContext reactContext) { - super(reactContext); - } + public MarkerView(ReactContext reactContext) { + super(reactContext); + } - @ReactProp(name = "refX") - public void setRefX(Dynamic refX) { - mRefX = SVGLength.from(refX); - invalidate(); - } + @ReactProp(name = "refX") + public void setRefX(Dynamic refX) { + mRefX = SVGLength.from(refX); + invalidate(); + } public void setRefX(String refX) { mRefX = SVGLength.from(refX); invalidate(); } - @ReactProp(name = "refY") - public void setRefY(Dynamic refY) { - mRefY = SVGLength.from(refY); - invalidate(); - } + @ReactProp(name = "refY") + public void setRefY(Dynamic refY) { + mRefY = SVGLength.from(refY); + invalidate(); + } public void setRefY(String refY) { mRefY = SVGLength.from(refY); invalidate(); } - @ReactProp(name = "markerWidth") - public void setMarkerWidth(Dynamic markerWidth) { - mMarkerWidth = SVGLength.from(markerWidth); - invalidate(); - } + @ReactProp(name = "markerWidth") + public void setMarkerWidth(Dynamic markerWidth) { + mMarkerWidth = SVGLength.from(markerWidth); + invalidate(); + } public void setMarkerWidth(String markerWidth) { mMarkerWidth = SVGLength.from(markerWidth); invalidate(); } - @ReactProp(name = "markerHeight") - public void setMarkerHeight(Dynamic markerHeight) { - mMarkerHeight = SVGLength.from(markerHeight); - invalidate(); - } + @ReactProp(name = "markerHeight") + public void setMarkerHeight(Dynamic markerHeight) { + mMarkerHeight = SVGLength.from(markerHeight); + invalidate(); + } public void setMarkerHeight(String markerHeight) { mMarkerHeight = SVGLength.from(markerHeight); invalidate(); } - @ReactProp(name = "markerUnits") - public void setMarkerUnits(String markerUnits) { - mMarkerUnits = markerUnits; - invalidate(); - } + @ReactProp(name = "markerUnits") + public void setMarkerUnits(String markerUnits) { + mMarkerUnits = markerUnits; + invalidate(); + } - @ReactProp(name = "orient") - public void setOrient(String orient) { - mOrient = orient; - invalidate(); - } + @ReactProp(name = "orient") + public void setOrient(String orient) { + mOrient = orient; + invalidate(); + } - @ReactProp(name = "minX") - public void setMinX(float minX) { - mMinX = minX; - invalidate(); - } + @ReactProp(name = "minX") + public void setMinX(float minX) { + mMinX = minX; + invalidate(); + } - @ReactProp(name = "minY") - public void setMinY(float minY) { - mMinY = minY; - invalidate(); - } + @ReactProp(name = "minY") + public void setMinY(float minY) { + mMinY = minY; + invalidate(); + } - @ReactProp(name = "vbWidth") - public void setVbWidth(float vbWidth) { - mVbWidth = vbWidth; - invalidate(); - } + @ReactProp(name = "vbWidth") + public void setVbWidth(float vbWidth) { + mVbWidth = vbWidth; + invalidate(); + } - @ReactProp(name = "vbHeight") - public void setVbHeight(float vbHeight) { - mVbHeight = vbHeight; - invalidate(); - } + @ReactProp(name = "vbHeight") + public void setVbHeight(float vbHeight) { + mVbHeight = vbHeight; + invalidate(); + } - @ReactProp(name = "align") - public void setAlign(String align) { - mAlign = align; - invalidate(); - } + @ReactProp(name = "align") + public void setAlign(String align) { + mAlign = align; + invalidate(); + } - @ReactProp(name = "meetOrSlice") - public void setMeetOrSlice(int meetOrSlice) { - mMeetOrSlice = meetOrSlice; - invalidate(); - } + @ReactProp(name = "meetOrSlice") + public void setMeetOrSlice(int meetOrSlice) { + mMeetOrSlice = meetOrSlice; + invalidate(); + } - @Override - void saveDefinition() { - if (mName != null) { - SvgView svg = getSvgView(); - svg.defineMarker(this, mName); - for (int i = 0; i < getChildCount(); i++) { - View node = getChildAt(i); - if (node instanceof VirtualView) { - ((VirtualView)node).saveDefinition(); - } - } + @Override + void saveDefinition() { + if (mName != null) { + SvgView svg = getSvgView(); + svg.defineMarker(this, mName); + for (int i = 0; i < getChildCount(); i++) { + View node = getChildAt(i); + if (node instanceof VirtualView) { + ((VirtualView) node).saveDefinition(); } + } + } + } + + void renderMarker( + Canvas canvas, Paint paint, float opacity, RNSVGMarkerPosition position, float strokeWidth) { + int count = saveAndSetupCanvas(canvas, mCTM); + + markerTransform.reset(); + Point origin = position.origin; + markerTransform.setTranslate((float) origin.x * mScale, (float) origin.y * mScale); + + double markerAngle = "auto".equals(mOrient) ? -1 : Double.parseDouble(mOrient); + float degrees = 180 + (float) (markerAngle == -1 ? position.angle : markerAngle); + markerTransform.preRotate(degrees); + + boolean useStrokeWidth = "strokeWidth".equals(mMarkerUnits); + if (useStrokeWidth) { + markerTransform.preScale(strokeWidth, strokeWidth); } - void renderMarker(Canvas canvas, Paint paint, float opacity, RNSVGMarkerPosition position, float strokeWidth) { - int count = saveAndSetupCanvas(canvas, mCTM); - - markerTransform.reset(); - Point origin = position.origin; - markerTransform.setTranslate((float)origin.x * mScale, (float)origin.y * mScale); - - double markerAngle = "auto".equals(mOrient) ? -1 : Double.parseDouble(mOrient); - float degrees = 180 + (float) (markerAngle == -1 ? position.angle : markerAngle); - markerTransform.preRotate(degrees); - - boolean useStrokeWidth = "strokeWidth".equals(mMarkerUnits); - if (useStrokeWidth) { - markerTransform.preScale(strokeWidth, strokeWidth); - } - - double width = relativeOnWidth(mMarkerWidth) / mScale; - double height = relativeOnHeight(mMarkerHeight) / mScale; - RectF eRect = new RectF(0, 0, (float)width, (float)height); - if (mAlign != null) { - RectF vbRect = new RectF(mMinX * mScale, mMinY * mScale, (mMinX + mVbWidth) * mScale, (mMinY + mVbHeight) * mScale); - Matrix viewBoxMatrix = ViewBox.getTransform(vbRect, eRect, mAlign, mMeetOrSlice); - float[] values = new float[9]; - viewBoxMatrix.getValues(values); - markerTransform.preScale(values[Matrix.MSCALE_X], values[Matrix.MSCALE_Y]); - } - - double x = relativeOnWidth(mRefX); - double y = relativeOnHeight(mRefY); - markerTransform.preTranslate((float)-x, (float)-y); - - canvas.concat(markerTransform); - - drawGroup(canvas, paint, opacity); - - restoreCanvas(canvas, count); + double width = relativeOnWidth(mMarkerWidth) / mScale; + double height = relativeOnHeight(mMarkerHeight) / mScale; + RectF eRect = new RectF(0, 0, (float) width, (float) height); + if (mAlign != null) { + RectF vbRect = + new RectF( + mMinX * mScale, + mMinY * mScale, + (mMinX + mVbWidth) * mScale, + (mMinY + mVbHeight) * mScale); + Matrix viewBoxMatrix = ViewBox.getTransform(vbRect, eRect, mAlign, mMeetOrSlice); + float[] values = new float[9]; + viewBoxMatrix.getValues(values); + markerTransform.preScale(values[Matrix.MSCALE_X], values[Matrix.MSCALE_Y]); } + + double x = relativeOnWidth(mRefX); + double y = relativeOnHeight(mRefY); + markerTransform.preTranslate((float) -x, (float) -y); + + canvas.concat(markerTransform); + + drawGroup(canvas, paint, opacity); + + restoreCanvas(canvas, count); + } } diff --git a/android/src/main/java/com/horcrux/svg/MaskView.java b/android/src/main/java/com/horcrux/svg/MaskView.java index 70e99e88..50f982ff 100644 --- a/android/src/main/java/com/horcrux/svg/MaskView.java +++ b/android/src/main/java/com/horcrux/svg/MaskView.java @@ -6,140 +6,139 @@ * LICENSE file in the root directory of this source tree. */ - package com.horcrux.svg; import android.annotation.SuppressLint; import android.graphics.Matrix; - import com.facebook.common.logging.FLog; import com.facebook.react.bridge.Dynamic; import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.common.ReactConstants; import com.facebook.react.uimanager.annotations.ReactProp; - import javax.annotation.Nullable; @SuppressLint("ViewConstructor") class MaskView extends GroupView { - SVGLength mX; - SVGLength mY; - SVGLength mW; - SVGLength mH; + SVGLength mX; + SVGLength mY; + SVGLength mW; + SVGLength mH; - // TODO implement proper support for units - @SuppressWarnings({"FieldCanBeLocal", "unused"}) - private Brush.BrushUnits mMaskUnits; - @SuppressWarnings({"FieldCanBeLocal", "unused"}) - private Brush.BrushUnits mMaskContentUnits; + // TODO implement proper support for units + @SuppressWarnings({"FieldCanBeLocal", "unused"}) + private Brush.BrushUnits mMaskUnits; - private static final float[] sRawMatrix = new float[]{ + @SuppressWarnings({"FieldCanBeLocal", "unused"}) + private Brush.BrushUnits mMaskContentUnits; + + private static final float[] sRawMatrix = + new float[] { 1, 0, 0, 0, 1, 0, 0, 0, 1 - }; - private Matrix mMatrix = null; + }; + private Matrix mMatrix = null; - public MaskView(ReactContext reactContext) { - super(reactContext); - } + public MaskView(ReactContext reactContext) { + super(reactContext); + } - @ReactProp(name = "x") - public void setX(Dynamic x) { - mX = SVGLength.from(x); - invalidate(); - } + @ReactProp(name = "x") + public void setX(Dynamic x) { + mX = SVGLength.from(x); + invalidate(); + } public void setX(String x) { mX = SVGLength.from(x); invalidate(); } - @ReactProp(name = "y") - public void setY(Dynamic y) { - mY = SVGLength.from(y); - invalidate(); - } + @ReactProp(name = "y") + public void setY(Dynamic y) { + mY = SVGLength.from(y); + invalidate(); + } public void setY(String y) { mY = SVGLength.from(y); invalidate(); } - @ReactProp(name = "width") - public void setWidth(Dynamic width) { - mW = SVGLength.from(width); - invalidate(); - } + @ReactProp(name = "width") + public void setWidth(Dynamic width) { + mW = SVGLength.from(width); + invalidate(); + } public void setWidth(String width) { mW = SVGLength.from(width); invalidate(); } - @ReactProp(name = "height") - public void setHeight(Dynamic height) { - mH = SVGLength.from(height); - invalidate(); - } + @ReactProp(name = "height") + public void setHeight(Dynamic height) { + mH = SVGLength.from(height); + invalidate(); + } public void setHeight(String height) { mH = SVGLength.from(height); invalidate(); } - @ReactProp(name = "maskUnits") - public void setMaskUnits(int maskUnits) { - switch (maskUnits) { - case 0: - mMaskUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX; - break; - case 1: - mMaskUnits = Brush.BrushUnits.USER_SPACE_ON_USE; - break; + @ReactProp(name = "maskUnits") + public void setMaskUnits(int maskUnits) { + switch (maskUnits) { + case 0: + mMaskUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX; + break; + case 1: + mMaskUnits = Brush.BrushUnits.USER_SPACE_ON_USE; + break; + } + invalidate(); + } + + @ReactProp(name = "maskContentUnits") + public void setMaskContentUnits(int maskContentUnits) { + switch (maskContentUnits) { + case 0: + mMaskContentUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX; + break; + case 1: + mMaskContentUnits = Brush.BrushUnits.USER_SPACE_ON_USE; + break; + } + invalidate(); + } + + @ReactProp(name = "maskTransform") + public void setMaskTransform(@Nullable ReadableArray matrixArray) { + if (matrixArray != null) { + int matrixSize = PropHelper.toMatrixData(matrixArray, sRawMatrix, mScale); + if (matrixSize == 6) { + if (mMatrix == null) { + mMatrix = new Matrix(); } - invalidate(); + mMatrix.setValues(sRawMatrix); + } else if (matrixSize != -1) { + FLog.w(ReactConstants.TAG, "RNSVG: Transform matrices must be of size 6"); + } + } else { + mMatrix = null; } - @ReactProp(name = "maskContentUnits") - public void setMaskContentUnits(int maskContentUnits) { - switch (maskContentUnits) { - case 0: - mMaskContentUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX; - break; - case 1: - mMaskContentUnits = Brush.BrushUnits.USER_SPACE_ON_USE; - break; - } - invalidate(); - } + invalidate(); + } - @ReactProp(name = "maskTransform") - public void setMaskTransform(@Nullable ReadableArray matrixArray) { - if (matrixArray != null) { - int matrixSize = PropHelper.toMatrixData(matrixArray, sRawMatrix, mScale); - if (matrixSize == 6) { - if (mMatrix == null) { - mMatrix = new Matrix(); - } - mMatrix.setValues(sRawMatrix); - } else if (matrixSize != -1) { - FLog.w(ReactConstants.TAG, "RNSVG: Transform matrices must be of size 6"); - } - } else { - mMatrix = null; - } - - invalidate(); - } - - @Override - void saveDefinition() { - if (mName != null) { - SvgView svg = getSvgView(); - svg.defineMask(this, mName); - } + @Override + void saveDefinition() { + if (mName != null) { + SvgView svg = getSvgView(); + svg.defineMask(this, mName); } + } } diff --git a/android/src/main/java/com/horcrux/svg/PathParser.java b/android/src/main/java/com/horcrux/svg/PathParser.java index f2546c54..f618030e 100644 --- a/android/src/main/java/com/horcrux/svg/PathParser.java +++ b/android/src/main/java/com/horcrux/svg/PathParser.java @@ -2,609 +2,678 @@ package com.horcrux.svg; import android.graphics.Path; import android.graphics.RectF; - import java.util.ArrayList; class PathElement { - ElementType type; - Point[] points; - PathElement(ElementType type, Point[] points) { - this.type = type; - this.points = points; - } + ElementType type; + Point[] points; + + PathElement(ElementType type, Point[] points) { + this.type = type; + this.points = points; + } } class PathParser { - static float mScale; + static float mScale; - private static int i; - private static int l; - private static String s; - private static Path mPath; - static ArrayList elements; + private static int i; + private static int l; + private static String s; + private static Path mPath; + static ArrayList elements; - private static float mPenX; - private static float mPenY; - private static float mPivotX; - private static float mPivotY; - private static float mPenDownX; - private static float mPenDownY; - private static boolean mPenDown; + private static float mPenX; + private static float mPenY; + private static float mPivotX; + private static float mPivotY; + private static float mPenDownX; + private static float mPenDownY; + private static boolean mPenDown; - static Path parse(String d) { - elements = new ArrayList<>(); - mPath = new Path(); - if(d == null){ - return mPath; - } - char prev_cmd = ' '; - l = d.length(); - s = d; - i = 0; + static Path parse(String d) { + elements = new ArrayList<>(); + mPath = new Path(); + if (d == null) { + return mPath; + } + char prev_cmd = ' '; + l = d.length(); + s = d; + i = 0; - mPenX = 0f; - mPenY = 0f; - mPivotX = 0f; - mPivotY = 0f; - mPenDownX = 0f; - mPenDownY = 0f; - mPenDown = false; + mPenX = 0f; + mPenY = 0f; + mPivotX = 0f; + mPivotY = 0f; + mPenDownX = 0f; + mPenDownY = 0f; + mPenDown = false; - while (i < l) { - skip_spaces(); + while (i < l) { + skip_spaces(); - if (i >= l) { - break; - } + if (i >= l) { + break; + } - boolean has_prev_cmd = prev_cmd != ' '; - char first_char = s.charAt(i); + boolean has_prev_cmd = prev_cmd != ' '; + char first_char = s.charAt(i); - if (!has_prev_cmd && first_char != 'M' && first_char != 'm') { - // The first segment must be a MoveTo. - throw new Error(String.format("Unexpected character '%c' (i=%d, s=%s)", first_char, i, s)); - } - - // TODO: simplify - boolean is_implicit_move_to; - char cmd; - if (is_cmd(first_char)) { - is_implicit_move_to = false; - cmd = first_char; - i += 1; - } else if (is_number_start(first_char) && has_prev_cmd) { - if (prev_cmd == 'Z' || prev_cmd == 'z') { - // ClosePath cannot be followed by a number. - throw new Error(String.format("Unexpected number after 'z' (s=%s)", s)); - } - - if (prev_cmd == 'M' || prev_cmd == 'm') { - // 'If a moveto is followed by multiple pairs of coordinates, - // the subsequent pairs are treated as implicit lineto commands.' - // So we parse them as LineTo. - is_implicit_move_to = true; - if (is_absolute(prev_cmd)) { - cmd = 'L'; - } else { - cmd = 'l'; - } - } else { - is_implicit_move_to = false; - cmd = prev_cmd; - } - } else { - throw new Error(String.format("Unexpected character '%c' (i=%d, s=%s)", first_char, i, s)); - } - - boolean absolute = is_absolute(cmd); - switch (cmd) { - case 'm': { - move(parse_list_number(), parse_list_number()); - break; - } - case 'M': { - moveTo(parse_list_number(), parse_list_number()); - break; - } - case 'l': { - line(parse_list_number(), parse_list_number()); - break; - } - case 'L': { - lineTo(parse_list_number(), parse_list_number()); - break; - } - case 'h': { - line(parse_list_number(), 0); - break; - } - case 'H': { - lineTo(parse_list_number(), mPenY); - break; - } - case 'v': { - line(0, parse_list_number()); - break; - } - case 'V': { - lineTo(mPenX, parse_list_number()); - break; - } - case 'c': { - curve(parse_list_number(), parse_list_number(), parse_list_number(), parse_list_number(), parse_list_number(), parse_list_number()); - break; - } - case 'C': { - curveTo(parse_list_number(), parse_list_number(), parse_list_number(), parse_list_number(), parse_list_number(), parse_list_number()); - break; - } - case 's': { - smoothCurve(parse_list_number(), parse_list_number(), parse_list_number(), parse_list_number()); - break; - } - case 'S': { - smoothCurveTo(parse_list_number(), parse_list_number(), parse_list_number(), parse_list_number()); - break; - } - case 'q': { - quadraticBezierCurve(parse_list_number(), parse_list_number(), parse_list_number(), parse_list_number()); - break; - } - case 'Q': { - quadraticBezierCurveTo(parse_list_number(), parse_list_number(), parse_list_number(), parse_list_number()); - break; - } - case 't': { - smoothQuadraticBezierCurve(parse_list_number(), parse_list_number()); - break; - } - case 'T': { - smoothQuadraticBezierCurveTo(parse_list_number(), parse_list_number()); - break; - } - case 'a': { - arc(parse_list_number(), parse_list_number(), parse_list_number(), parse_flag(), parse_flag(), parse_list_number(), parse_list_number()); - break; - } - case 'A': { - arcTo(parse_list_number(), parse_list_number(), parse_list_number(), parse_flag(), parse_flag(), parse_list_number(), parse_list_number()); - break; - } - case 'z': - case 'Z': { - close(); - break; - } - default: { - throw new Error(String.format("Unexpected comand '%c' (s=%s)", cmd, s)); - } - } - - - if (is_implicit_move_to) { - if (absolute) { - prev_cmd = 'M'; - } else { - prev_cmd = 'm'; - } - } else { - prev_cmd = cmd; - } + if (!has_prev_cmd && first_char != 'M' && first_char != 'm') { + // The first segment must be a MoveTo. + throw new Error(String.format("Unexpected character '%c' (i=%d, s=%s)", first_char, i, s)); + } + // TODO: simplify + boolean is_implicit_move_to; + char cmd; + if (is_cmd(first_char)) { + is_implicit_move_to = false; + cmd = first_char; + i += 1; + } else if (is_number_start(first_char) && has_prev_cmd) { + if (prev_cmd == 'Z' || prev_cmd == 'z') { + // ClosePath cannot be followed by a number. + throw new Error(String.format("Unexpected number after 'z' (s=%s)", s)); } - return mPath; - } - - private static void move(float x, float y) { - moveTo(x + mPenX, y + mPenY); - } - - private static void moveTo(float x, float y) { - //FLog.w(ReactConstants.TAG, "move x: " + x + " y: " + y); - mPenDownX = mPivotX = mPenX = x; - mPenDownY = mPivotY = mPenY = y; - mPath.moveTo(x * mScale, y * mScale); - elements.add(new PathElement(ElementType.kCGPathElementMoveToPoint, new Point[]{new Point(x,y)})); - } - - private static void line(float x, float y) { - lineTo(x + mPenX, y + mPenY); - } - - private static void lineTo(float x, float y) { - //FLog.w(ReactConstants.TAG, "line x: " + x + " y: " + y); - setPenDown(); - mPivotX = mPenX = x; - mPivotY = mPenY = y; - mPath.lineTo(x * mScale, y * mScale); - elements.add(new PathElement(ElementType.kCGPathElementAddLineToPoint, new Point[]{new Point(x,y)})); - } - - private static void curve(float c1x, float c1y, float c2x, float c2y, float ex, float ey) { - curveTo(c1x + mPenX, c1y + mPenY, c2x + mPenX, c2y + mPenY, ex + mPenX, ey + mPenY); - } - - private static void curveTo(float c1x, float c1y, float c2x, float c2y, float ex, float ey) { - //FLog.w(ReactConstants.TAG, "curve c1x: " + c1x + " c1y: " + c1y + "ex: " + ex + " ey: " + ey); - mPivotX = c2x; - mPivotY = c2y; - cubicTo(c1x, c1y, c2x, c2y, ex, ey); - } - - private static void cubicTo(float c1x, float c1y, float c2x, float c2y, float ex, float ey) { - setPenDown(); - mPenX = ex; - mPenY = ey; - mPath.cubicTo(c1x * mScale, c1y * mScale, c2x * mScale, c2y * mScale, ex * mScale, ey * mScale); - elements.add(new PathElement(ElementType.kCGPathElementAddCurveToPoint, new Point[]{new Point(c1x, c1y), new Point(c2x, c2y), new Point(ex, ey)})); - } - - private static void smoothCurve(float c1x, float c1y, float ex, float ey) { - smoothCurveTo(c1x + mPenX, c1y + mPenY, ex + mPenX, ey + mPenY); - } - - private static void smoothCurveTo(float c1x, float c1y, float ex, float ey) { - //FLog.w(ReactConstants.TAG, "smoothcurve c1x: " + c1x + " c1y: " + c1y + "ex: " + ex + " ey: " + ey); - float c2x = c1x; - float c2y = c1y; - c1x = (mPenX * 2) - mPivotX; - c1y = (mPenY * 2) - mPivotY; - mPivotX = c2x; - mPivotY = c2y; - cubicTo(c1x, c1y, c2x, c2y, ex, ey); - } - - private static void quadraticBezierCurve(float c1x, float c1y, float c2x, float c2y) { - quadraticBezierCurveTo(c1x + mPenX, c1y + mPenY, c2x + mPenX, c2y + mPenY); - } - - private static void quadraticBezierCurveTo(float c1x, float c1y, float c2x, float c2y) { - //FLog.w(ReactConstants.TAG, "quad c1x: " + c1x + " c1y: " + c1y + "c2x: " + c2x + " c2y: " + c2y); - mPivotX = c1x; - mPivotY = c1y; - float ex = c2x; - float ey = c2y; - c2x = (ex + c1x * 2) / 3; - c2y = (ey + c1y * 2) / 3; - c1x = (mPenX + c1x * 2) / 3; - c1y = (mPenY + c1y * 2) / 3; - cubicTo(c1x, c1y, c2x, c2y, ex, ey); - } - - private static void smoothQuadraticBezierCurve(float c1x, float c1y) { - smoothQuadraticBezierCurveTo(c1x + mPenX, c1y + mPenY); - } - - private static void smoothQuadraticBezierCurveTo(float c1x, float c1y) { - //FLog.w(ReactConstants.TAG, "smoothquad c1x: " + c1x + " c1y: " + c1y); - float c2x = c1x; - float c2y = c1y; - c1x = (mPenX * 2) - mPivotX; - c1y = (mPenY * 2) - mPivotY; - quadraticBezierCurveTo(c1x, c1y, c2x, c2y); - } - - private static void arc(float rx, float ry, float rotation, boolean outer, boolean clockwise, float x, float y) { - arcTo(rx, ry, rotation, outer, clockwise, x + mPenX, y + mPenY); - } - - private static void arcTo(float rx, float ry, float rotation, boolean outer, boolean clockwise, float x, float y) { - //FLog.w(ReactConstants.TAG, "arc rx: " + rx + " ry: " + ry + " rotation: " + rotation + " outer: " + outer + " clockwise: " + clockwise + " x: " + x + " y: " + y); - float tX = mPenX; - float tY = mPenY; - - ry = Math.abs(ry == 0 ? (rx == 0 ? (y - tY) : rx) : ry); - rx = Math.abs(rx == 0 ? (x - tX) : rx); - - if (rx == 0 || ry == 0 || (x == tX && y == tY)) { - lineTo(x, y); - return; - } - - float rad = (float) Math.toRadians(rotation); - float cos = (float) Math.cos(rad); - float sin = (float) Math.sin(rad); - x -= tX; - y -= tY; - - // Ellipse Center - float cx = cos * x / 2 + sin * y / 2; - float cy = -sin * x / 2 + cos * y / 2; - float rxry = rx * rx * ry * ry; - float rycx = ry * ry * cx * cx; - float rxcy = rx * rx * cy * cy; - float a = rxry - rxcy - rycx; - - if (a < 0) { - a = (float) Math.sqrt(1 - a / rxry); - rx *= a; - ry *= a; - cx = x / 2; - cy = y / 2; + if (prev_cmd == 'M' || prev_cmd == 'm') { + // 'If a moveto is followed by multiple pairs of coordinates, + // the subsequent pairs are treated as implicit lineto commands.' + // So we parse them as LineTo. + is_implicit_move_to = true; + if (is_absolute(prev_cmd)) { + cmd = 'L'; + } else { + cmd = 'l'; + } } else { - a = (float) Math.sqrt(a / (rxcy + rycx)); - - if (outer == clockwise) { - a = -a; - } - float cxd = -a * cy * rx / ry; - float cyd = a * cx * ry / rx; - cx = cos * cxd - sin * cyd + x / 2; - cy = sin * cxd + cos * cyd + y / 2; + is_implicit_move_to = false; + cmd = prev_cmd; } + } else { + throw new Error(String.format("Unexpected character '%c' (i=%d, s=%s)", first_char, i, s)); + } - // Rotation + Scale Transform - float xx = cos / rx; - float yx = sin / rx; - float xy = -sin / ry; - float yy = cos / ry; + boolean absolute = is_absolute(cmd); + switch (cmd) { + case 'm': + { + move(parse_list_number(), parse_list_number()); + break; + } + case 'M': + { + moveTo(parse_list_number(), parse_list_number()); + break; + } + case 'l': + { + line(parse_list_number(), parse_list_number()); + break; + } + case 'L': + { + lineTo(parse_list_number(), parse_list_number()); + break; + } + case 'h': + { + line(parse_list_number(), 0); + break; + } + case 'H': + { + lineTo(parse_list_number(), mPenY); + break; + } + case 'v': + { + line(0, parse_list_number()); + break; + } + case 'V': + { + lineTo(mPenX, parse_list_number()); + break; + } + case 'c': + { + curve( + parse_list_number(), + parse_list_number(), + parse_list_number(), + parse_list_number(), + parse_list_number(), + parse_list_number()); + break; + } + case 'C': + { + curveTo( + parse_list_number(), + parse_list_number(), + parse_list_number(), + parse_list_number(), + parse_list_number(), + parse_list_number()); + break; + } + case 's': + { + smoothCurve( + parse_list_number(), parse_list_number(), parse_list_number(), parse_list_number()); + break; + } + case 'S': + { + smoothCurveTo( + parse_list_number(), parse_list_number(), parse_list_number(), parse_list_number()); + break; + } + case 'q': + { + quadraticBezierCurve( + parse_list_number(), parse_list_number(), parse_list_number(), parse_list_number()); + break; + } + case 'Q': + { + quadraticBezierCurveTo( + parse_list_number(), parse_list_number(), parse_list_number(), parse_list_number()); + break; + } + case 't': + { + smoothQuadraticBezierCurve(parse_list_number(), parse_list_number()); + break; + } + case 'T': + { + smoothQuadraticBezierCurveTo(parse_list_number(), parse_list_number()); + break; + } + case 'a': + { + arc( + parse_list_number(), + parse_list_number(), + parse_list_number(), + parse_flag(), + parse_flag(), + parse_list_number(), + parse_list_number()); + break; + } + case 'A': + { + arcTo( + parse_list_number(), + parse_list_number(), + parse_list_number(), + parse_flag(), + parse_flag(), + parse_list_number(), + parse_list_number()); + break; + } + case 'z': + case 'Z': + { + close(); + break; + } + default: + { + throw new Error(String.format("Unexpected comand '%c' (s=%s)", cmd, s)); + } + } - // Start and End Angle - float sa = (float) Math.atan2(xy * -cx + yy * -cy, xx * -cx + yx * -cy); - float ea = (float) Math.atan2(xy * (x - cx) + yy * (y - cy), xx * (x - cx) + yx * (y - cy)); - - cx += tX; - cy += tY; - x += tX; - y += tY; - - setPenDown(); - - mPenX = mPivotX = x; - mPenY = mPivotY = y; - - if (rx != ry || rad != 0f) { - arcToBezier(cx, cy, rx, ry, sa, ea, clockwise, rad); + if (is_implicit_move_to) { + if (absolute) { + prev_cmd = 'M'; } else { - - float start = (float) Math.toDegrees(sa); - float end = (float) Math.toDegrees(ea); - float sweep = Math.abs((start - end) % 360); - - if (outer) { - if (sweep < 180) { - sweep = 360 - sweep; - } - } else { - if (sweep > 180) { - sweep = 360 - sweep; - } - } - - if (!clockwise) { - sweep = -sweep; - } - - RectF oval = new RectF( - (cx - rx) * mScale, - (cy - rx) * mScale, - (cx + rx) * mScale, - (cy + rx) * mScale); - - mPath.arcTo(oval, start, sweep); - elements.add(new PathElement(ElementType.kCGPathElementAddCurveToPoint, new Point[]{new Point(x, y)})); + prev_cmd = 'm'; } + } else { + prev_cmd = cmd; + } } - private static void close() { - if (mPenDown) { - mPenX = mPenDownX; - mPenY = mPenDownY; - mPenDown = false; - mPath.close(); - elements.add(new PathElement(ElementType.kCGPathElementCloseSubpath, new Point[]{new Point(mPenX, mPenY)})); + return mPath; + } + + private static void move(float x, float y) { + moveTo(x + mPenX, y + mPenY); + } + + private static void moveTo(float x, float y) { + // FLog.w(ReactConstants.TAG, "move x: " + x + " y: " + y); + mPenDownX = mPivotX = mPenX = x; + mPenDownY = mPivotY = mPenY = y; + mPath.moveTo(x * mScale, y * mScale); + elements.add( + new PathElement(ElementType.kCGPathElementMoveToPoint, new Point[] {new Point(x, y)})); + } + + private static void line(float x, float y) { + lineTo(x + mPenX, y + mPenY); + } + + private static void lineTo(float x, float y) { + // FLog.w(ReactConstants.TAG, "line x: " + x + " y: " + y); + setPenDown(); + mPivotX = mPenX = x; + mPivotY = mPenY = y; + mPath.lineTo(x * mScale, y * mScale); + elements.add( + new PathElement(ElementType.kCGPathElementAddLineToPoint, new Point[] {new Point(x, y)})); + } + + private static void curve(float c1x, float c1y, float c2x, float c2y, float ex, float ey) { + curveTo(c1x + mPenX, c1y + mPenY, c2x + mPenX, c2y + mPenY, ex + mPenX, ey + mPenY); + } + + private static void curveTo(float c1x, float c1y, float c2x, float c2y, float ex, float ey) { + // FLog.w(ReactConstants.TAG, "curve c1x: " + c1x + " c1y: " + c1y + "ex: " + ex + " ey: " + + // ey); + mPivotX = c2x; + mPivotY = c2y; + cubicTo(c1x, c1y, c2x, c2y, ex, ey); + } + + private static void cubicTo(float c1x, float c1y, float c2x, float c2y, float ex, float ey) { + setPenDown(); + mPenX = ex; + mPenY = ey; + mPath.cubicTo(c1x * mScale, c1y * mScale, c2x * mScale, c2y * mScale, ex * mScale, ey * mScale); + elements.add( + new PathElement( + ElementType.kCGPathElementAddCurveToPoint, + new Point[] {new Point(c1x, c1y), new Point(c2x, c2y), new Point(ex, ey)})); + } + + private static void smoothCurve(float c1x, float c1y, float ex, float ey) { + smoothCurveTo(c1x + mPenX, c1y + mPenY, ex + mPenX, ey + mPenY); + } + + private static void smoothCurveTo(float c1x, float c1y, float ex, float ey) { + // FLog.w(ReactConstants.TAG, "smoothcurve c1x: " + c1x + " c1y: " + c1y + "ex: " + ex + " ey: " + // + ey); + float c2x = c1x; + float c2y = c1y; + c1x = (mPenX * 2) - mPivotX; + c1y = (mPenY * 2) - mPivotY; + mPivotX = c2x; + mPivotY = c2y; + cubicTo(c1x, c1y, c2x, c2y, ex, ey); + } + + private static void quadraticBezierCurve(float c1x, float c1y, float c2x, float c2y) { + quadraticBezierCurveTo(c1x + mPenX, c1y + mPenY, c2x + mPenX, c2y + mPenY); + } + + private static void quadraticBezierCurveTo(float c1x, float c1y, float c2x, float c2y) { + // FLog.w(ReactConstants.TAG, "quad c1x: " + c1x + " c1y: " + c1y + "c2x: " + c2x + " c2y: " + + // c2y); + mPivotX = c1x; + mPivotY = c1y; + float ex = c2x; + float ey = c2y; + c2x = (ex + c1x * 2) / 3; + c2y = (ey + c1y * 2) / 3; + c1x = (mPenX + c1x * 2) / 3; + c1y = (mPenY + c1y * 2) / 3; + cubicTo(c1x, c1y, c2x, c2y, ex, ey); + } + + private static void smoothQuadraticBezierCurve(float c1x, float c1y) { + smoothQuadraticBezierCurveTo(c1x + mPenX, c1y + mPenY); + } + + private static void smoothQuadraticBezierCurveTo(float c1x, float c1y) { + // FLog.w(ReactConstants.TAG, "smoothquad c1x: " + c1x + " c1y: " + c1y); + float c2x = c1x; + float c2y = c1y; + c1x = (mPenX * 2) - mPivotX; + c1y = (mPenY * 2) - mPivotY; + quadraticBezierCurveTo(c1x, c1y, c2x, c2y); + } + + private static void arc( + float rx, float ry, float rotation, boolean outer, boolean clockwise, float x, float y) { + arcTo(rx, ry, rotation, outer, clockwise, x + mPenX, y + mPenY); + } + + private static void arcTo( + float rx, float ry, float rotation, boolean outer, boolean clockwise, float x, float y) { + // FLog.w(ReactConstants.TAG, "arc rx: " + rx + " ry: " + ry + " rotation: " + rotation + " + // outer: " + outer + " clockwise: " + clockwise + " x: " + x + " y: " + y); + float tX = mPenX; + float tY = mPenY; + + ry = Math.abs(ry == 0 ? (rx == 0 ? (y - tY) : rx) : ry); + rx = Math.abs(rx == 0 ? (x - tX) : rx); + + if (rx == 0 || ry == 0 || (x == tX && y == tY)) { + lineTo(x, y); + return; + } + + float rad = (float) Math.toRadians(rotation); + float cos = (float) Math.cos(rad); + float sin = (float) Math.sin(rad); + x -= tX; + y -= tY; + + // Ellipse Center + float cx = cos * x / 2 + sin * y / 2; + float cy = -sin * x / 2 + cos * y / 2; + float rxry = rx * rx * ry * ry; + float rycx = ry * ry * cx * cx; + float rxcy = rx * rx * cy * cy; + float a = rxry - rxcy - rycx; + + if (a < 0) { + a = (float) Math.sqrt(1 - a / rxry); + rx *= a; + ry *= a; + cx = x / 2; + cy = y / 2; + } else { + a = (float) Math.sqrt(a / (rxcy + rycx)); + + if (outer == clockwise) { + a = -a; + } + float cxd = -a * cy * rx / ry; + float cyd = a * cx * ry / rx; + cx = cos * cxd - sin * cyd + x / 2; + cy = sin * cxd + cos * cyd + y / 2; + } + + // Rotation + Scale Transform + float xx = cos / rx; + float yx = sin / rx; + float xy = -sin / ry; + float yy = cos / ry; + + // Start and End Angle + float sa = (float) Math.atan2(xy * -cx + yy * -cy, xx * -cx + yx * -cy); + float ea = (float) Math.atan2(xy * (x - cx) + yy * (y - cy), xx * (x - cx) + yx * (y - cy)); + + cx += tX; + cy += tY; + x += tX; + y += tY; + + setPenDown(); + + mPenX = mPivotX = x; + mPenY = mPivotY = y; + + if (rx != ry || rad != 0f) { + arcToBezier(cx, cy, rx, ry, sa, ea, clockwise, rad); + } else { + + float start = (float) Math.toDegrees(sa); + float end = (float) Math.toDegrees(ea); + float sweep = Math.abs((start - end) % 360); + + if (outer) { + if (sweep < 180) { + sweep = 360 - sweep; } - } - - private static void arcToBezier(float cx, float cy, float rx, float ry, float sa, float ea, boolean clockwise, float rad) { - // Inverse Rotation + Scale Transform - float cos = (float) Math.cos(rad); - float sin = (float) Math.sin(rad); - float xx = cos * rx; - float yx = -sin * ry; - float xy = sin * rx; - float yy = cos * ry; - - // Bezier Curve Approximation - float arc = ea - sa; - if (arc < 0 && clockwise) { - arc += Math.PI * 2; - } else if (arc > 0 && !clockwise) { - arc -= Math.PI * 2; + } else { + if (sweep > 180) { + sweep = 360 - sweep; } + } - int n = (int) Math.ceil(Math.abs(round(arc / (Math.PI / 2)))); + if (!clockwise) { + sweep = -sweep; + } - float step = arc / n; - float k = (float) ((4 / 3.0) * Math.tan(step / 4)); + RectF oval = + new RectF((cx - rx) * mScale, (cy - rx) * mScale, (cx + rx) * mScale, (cy + rx) * mScale); - float x = (float) Math.cos(sa); - float y = (float) Math.sin(sa); + mPath.arcTo(oval, start, sweep); + elements.add( + new PathElement( + ElementType.kCGPathElementAddCurveToPoint, new Point[] {new Point(x, y)})); + } + } - for (int i = 0; i < n; i++) { - float cp1x = x - k * y; - float cp1y = y + k * x; + private static void close() { + if (mPenDown) { + mPenX = mPenDownX; + mPenY = mPenDownY; + mPenDown = false; + mPath.close(); + elements.add( + new PathElement( + ElementType.kCGPathElementCloseSubpath, new Point[] {new Point(mPenX, mPenY)})); + } + } - sa += step; - x = (float) Math.cos(sa); - y = (float) Math.sin(sa); + private static void arcToBezier( + float cx, float cy, float rx, float ry, float sa, float ea, boolean clockwise, float rad) { + // Inverse Rotation + Scale Transform + float cos = (float) Math.cos(rad); + float sin = (float) Math.sin(rad); + float xx = cos * rx; + float yx = -sin * ry; + float xy = sin * rx; + float yy = cos * ry; - float cp2x = x + k * y; - float cp2y = y - k * x; - - float c1x = (cx + xx * cp1x + yx * cp1y); - float c1y = (cy + xy * cp1x + yy * cp1y); - float c2x = (cx + xx * cp2x + yx * cp2y); - float c2y = (cy + xy * cp2x + yy * cp2y); - float ex = (cx + xx * x + yx * y); - float ey = (cy + xy * x + yy * y); - - mPath.cubicTo(c1x * mScale, c1y * mScale, c2x * mScale, c2y * mScale, ex * mScale, ey * mScale); - elements.add(new PathElement(ElementType.kCGPathElementAddCurveToPoint, new Point[]{new Point(c1x, c1y), new Point(c2x, c2y), new Point(ex, ey)})); - } + // Bezier Curve Approximation + float arc = ea - sa; + if (arc < 0 && clockwise) { + arc += Math.PI * 2; + } else if (arc > 0 && !clockwise) { + arc -= Math.PI * 2; } - private static void setPenDown() { - if (!mPenDown) { - mPenDownX = mPenX; - mPenDownY = mPenY; - mPenDown = true; - } + int n = (int) Math.ceil(Math.abs(round(arc / (Math.PI / 2)))); + + float step = arc / n; + float k = (float) ((4 / 3.0) * Math.tan(step / 4)); + + float x = (float) Math.cos(sa); + float y = (float) Math.sin(sa); + + for (int i = 0; i < n; i++) { + float cp1x = x - k * y; + float cp1y = y + k * x; + + sa += step; + x = (float) Math.cos(sa); + y = (float) Math.sin(sa); + + float cp2x = x + k * y; + float cp2y = y - k * x; + + float c1x = (cx + xx * cp1x + yx * cp1y); + float c1y = (cy + xy * cp1x + yy * cp1y); + float c2x = (cx + xx * cp2x + yx * cp2y); + float c2y = (cy + xy * cp2x + yy * cp2y); + float ex = (cx + xx * x + yx * y); + float ey = (cy + xy * x + yy * y); + + mPath.cubicTo( + c1x * mScale, c1y * mScale, c2x * mScale, c2y * mScale, ex * mScale, ey * mScale); + elements.add( + new PathElement( + ElementType.kCGPathElementAddCurveToPoint, + new Point[] {new Point(c1x, c1y), new Point(c2x, c2y), new Point(ex, ey)})); } + } - private static double round(double val) { - double multiplier = Math.pow(10, 4); - return Math.round(val * multiplier) / multiplier; + private static void setPenDown() { + if (!mPenDown) { + mPenDownX = mPenX; + mPenDownY = mPenY; + mPenDown = true; } + } - private static void skip_spaces() { - while (i < l && Character.isWhitespace(s.charAt(i))) i++; + private static double round(double val) { + double multiplier = Math.pow(10, 4); + return Math.round(val * multiplier) / multiplier; + } + + private static void skip_spaces() { + while (i < l && Character.isWhitespace(s.charAt(i))) i++; + } + + private static boolean is_cmd(char c) { + switch (c) { + case 'M': + case 'm': + case 'Z': + case 'z': + case 'L': + case 'l': + case 'H': + case 'h': + case 'V': + case 'v': + case 'C': + case 'c': + case 'S': + case 's': + case 'Q': + case 'q': + case 'T': + case 't': + case 'A': + case 'a': + return true; } + return false; + } - private static boolean is_cmd(char c) { - switch (c) { - case 'M': - case 'm': - case 'Z': - case 'z': - case 'L': - case 'l': - case 'H': - case 'h': - case 'V': - case 'v': - case 'C': - case 'c': - case 'S': - case 's': - case 'Q': - case 'q': - case 'T': - case 't': - case 'A': - case 'a': - return true; - } - return false; - } + private static boolean is_number_start(char c) { + return (c >= '0' && c <= '9') || c == '.' || c == '-' || c == '+'; + } - private static boolean is_number_start(char c) { - return (c >= '0' && c <= '9') || c == '.' || c == '-' || c == '+'; - } + private static boolean is_absolute(char c) { + return Character.isUpperCase(c); + } - private static boolean is_absolute(char c) { - return Character.isUpperCase(c); - } + // By the SVG spec 'large-arc' and 'sweep' must contain only one char + // and can be written without any separators, e.g.: 10 20 30 01 10 20. + private static boolean parse_flag() { + skip_spaces(); - // By the SVG spec 'large-arc' and 'sweep' must contain only one char - // and can be written without any separators, e.g.: 10 20 30 01 10 20. - private static boolean parse_flag() { - skip_spaces(); - - char c = s.charAt(i); - switch (c) { - case '0': - case '1': { - i += 1; - if (i < l && s.charAt(i) == ',') { - i += 1; - } - skip_spaces(); - break; - } - default: - throw new Error(String.format("Unexpected flag '%c' (i=%d, s=%s)", c, i, s)); - } - - return c == '1'; - } - - private static float parse_list_number() { - if (i == l) { - throw new Error(String.format("Unexpected end (s=%s)", s)); - } - - float n = parse_number(); - skip_spaces(); - parse_list_separator(); - - return n; - } - - private static float parse_number() { - // Strip off leading whitespaces. - skip_spaces(); - - if (i == l) { - throw new Error(String.format("Unexpected end (s=%s)", s)); - } - - int start = i; - - char c = s.charAt(i); - - // Consume sign. - if (c == '-' || c == '+') { + char c = s.charAt(i); + switch (c) { + case '0': + case '1': + { + i += 1; + if (i < l && s.charAt(i) == ',') { i += 1; - c = s.charAt(i); + } + skip_spaces(); + break; } - - // Consume integer. - if (c >= '0' && c <= '9') { - skip_digits(); - if (i < l) { - c = s.charAt(i); - } - } else if (c != '.') { - throw new Error(String.format("Invalid number formating character '%c' (i=%d, s=%s)", c ,i, s)); - } - - // Consume fraction. - if (c == '.') { - i += 1; - skip_digits(); - if (i < l) { - c = s.charAt(i); - } - } - - if ((c == 'e' || c == 'E') && i + 1 < l) { - char c2 = s.charAt(i + 1); - // Check for `em`/`ex`. - if (c2 != 'm' && c2 != 'x') { - i += 1; - c = s.charAt(i); - - if (c == '+' || c == '-') { - i += 1; - skip_digits(); - } else if (c >= '0' && c <= '9') { - skip_digits(); - } else { - throw new Error(String.format("Invalid number formating character '%c' (i=%d, s=%s)", c, i, s)); - } - } - } - - String num = s.substring(start, i); - float n = Float.parseFloat(num); - - // inf, nan, etc. are an error. - if (Float.isInfinite(n) || Float.isNaN(n)) { - throw new Error(String.format("Invalid number '%s' (start=%d, i=%d, s=%s)", num, start, i, s)); - } - - return n; + default: + throw new Error(String.format("Unexpected flag '%c' (i=%d, s=%s)", c, i, s)); } - private static void parse_list_separator() { - if (i < l && s.charAt(i) == ',') { - i += 1; - } + return c == '1'; + } + + private static float parse_list_number() { + if (i == l) { + throw new Error(String.format("Unexpected end (s=%s)", s)); } - private static void skip_digits() { - while (i < l && Character.isDigit(s.charAt(i))) i++; + float n = parse_number(); + skip_spaces(); + parse_list_separator(); + + return n; + } + + private static float parse_number() { + // Strip off leading whitespaces. + skip_spaces(); + + if (i == l) { + throw new Error(String.format("Unexpected end (s=%s)", s)); } + + int start = i; + + char c = s.charAt(i); + + // Consume sign. + if (c == '-' || c == '+') { + i += 1; + c = s.charAt(i); + } + + // Consume integer. + if (c >= '0' && c <= '9') { + skip_digits(); + if (i < l) { + c = s.charAt(i); + } + } else if (c != '.') { + throw new Error( + String.format("Invalid number formating character '%c' (i=%d, s=%s)", c, i, s)); + } + + // Consume fraction. + if (c == '.') { + i += 1; + skip_digits(); + if (i < l) { + c = s.charAt(i); + } + } + + if ((c == 'e' || c == 'E') && i + 1 < l) { + char c2 = s.charAt(i + 1); + // Check for `em`/`ex`. + if (c2 != 'm' && c2 != 'x') { + i += 1; + c = s.charAt(i); + + if (c == '+' || c == '-') { + i += 1; + skip_digits(); + } else if (c >= '0' && c <= '9') { + skip_digits(); + } else { + throw new Error( + String.format("Invalid number formating character '%c' (i=%d, s=%s)", c, i, s)); + } + } + } + + String num = s.substring(start, i); + float n = Float.parseFloat(num); + + // inf, nan, etc. are an error. + if (Float.isInfinite(n) || Float.isNaN(n)) { + throw new Error( + String.format("Invalid number '%s' (start=%d, i=%d, s=%s)", num, start, i, s)); + } + + return n; + } + + private static void parse_list_separator() { + if (i < l && s.charAt(i) == ',') { + i += 1; + } + } + + private static void skip_digits() { + while (i < l && Character.isDigit(s.charAt(i))) i++; + } } diff --git a/android/src/main/java/com/horcrux/svg/PathView.java b/android/src/main/java/com/horcrux/svg/PathView.java index 20c5fee0..34ac697f 100644 --- a/android/src/main/java/com/horcrux/svg/PathView.java +++ b/android/src/main/java/com/horcrux/svg/PathView.java @@ -6,37 +6,34 @@ * LICENSE file in the root directory of this source tree. */ - package com.horcrux.svg; import android.annotation.SuppressLint; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; - import com.facebook.react.bridge.ReactContext; import com.facebook.react.uimanager.annotations.ReactProp; @SuppressLint("ViewConstructor") class PathView extends RenderableView { - private Path mPath; + private Path mPath; - public PathView(ReactContext reactContext) { - super(reactContext); - PathParser.mScale = mScale; - mPath = new Path(); - } + public PathView(ReactContext reactContext) { + super(reactContext); + PathParser.mScale = mScale; + mPath = new Path(); + } - @ReactProp(name = "d") - public void setD(String d) { - mPath = PathParser.parse(d); - elements = PathParser.elements; - invalidate(); - } - - @Override - Path getPath(Canvas canvas, Paint paint) { - return mPath; - } + @ReactProp(name = "d") + public void setD(String d) { + mPath = PathParser.parse(d); + elements = PathParser.elements; + invalidate(); + } + @Override + Path getPath(Canvas canvas, Paint paint) { + return mPath; + } } diff --git a/android/src/main/java/com/horcrux/svg/PatternView.java b/android/src/main/java/com/horcrux/svg/PatternView.java index d4fd40a6..b448abcc 100644 --- a/android/src/main/java/com/horcrux/svg/PatternView.java +++ b/android/src/main/java/com/horcrux/svg/PatternView.java @@ -6,197 +6,197 @@ * LICENSE file in the root directory of this source tree. */ - package com.horcrux.svg; import android.annotation.SuppressLint; import android.graphics.Matrix; import android.graphics.RectF; - import com.facebook.common.logging.FLog; import com.facebook.react.bridge.Dynamic; import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.common.ReactConstants; import com.facebook.react.uimanager.annotations.ReactProp; - import javax.annotation.Nullable; @SuppressLint("ViewConstructor") class PatternView extends GroupView { - private SVGLength mX; - private SVGLength mY; - private SVGLength mW; - private SVGLength mH; - private Brush.BrushUnits mPatternUnits; - private Brush.BrushUnits mPatternContentUnits; + private SVGLength mX; + private SVGLength mY; + private SVGLength mW; + private SVGLength mH; + private Brush.BrushUnits mPatternUnits; + private Brush.BrushUnits mPatternContentUnits; - private float mMinX; - private float mMinY; - private float mVbWidth; - private float mVbHeight; - String mAlign; - int mMeetOrSlice; + private float mMinX; + private float mMinY; + private float mVbWidth; + private float mVbHeight; + String mAlign; + int mMeetOrSlice; - private static final float[] sRawMatrix = new float[]{ + private static final float[] sRawMatrix = + new float[] { 1, 0, 0, 0, 1, 0, 0, 0, 1 - }; - private Matrix mMatrix = null; + }; + private Matrix mMatrix = null; - public PatternView(ReactContext reactContext) { - super(reactContext); - } + public PatternView(ReactContext reactContext) { + super(reactContext); + } - @ReactProp(name = "x") - public void setX(Dynamic x) { - mX = SVGLength.from(x); - invalidate(); - } + @ReactProp(name = "x") + public void setX(Dynamic x) { + mX = SVGLength.from(x); + invalidate(); + } public void setX(String x) { mX = SVGLength.from(x); invalidate(); } - @ReactProp(name = "y") - public void setY(Dynamic y) { - mY = SVGLength.from(y); - invalidate(); - } + @ReactProp(name = "y") + public void setY(Dynamic y) { + mY = SVGLength.from(y); + invalidate(); + } public void setY(String y) { mY = SVGLength.from(y); invalidate(); } - @ReactProp(name = "width") - public void setWidth(Dynamic width) { - mW = SVGLength.from(width); - invalidate(); - } + @ReactProp(name = "width") + public void setWidth(Dynamic width) { + mW = SVGLength.from(width); + invalidate(); + } public void setWidth(String width) { mW = SVGLength.from(width); invalidate(); } - @ReactProp(name = "height") - public void setHeight(Dynamic height) { - mH = SVGLength.from(height); - invalidate(); - } + + @ReactProp(name = "height") + public void setHeight(Dynamic height) { + mH = SVGLength.from(height); + invalidate(); + } public void setHeight(String height) { mH = SVGLength.from(height); invalidate(); } - @ReactProp(name = "patternUnits") - public void setPatternUnits(int patternUnits) { - switch (patternUnits) { - case 0: - mPatternUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX; - break; - case 1: - mPatternUnits = Brush.BrushUnits.USER_SPACE_ON_USE; - break; + @ReactProp(name = "patternUnits") + public void setPatternUnits(int patternUnits) { + switch (patternUnits) { + case 0: + mPatternUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX; + break; + case 1: + mPatternUnits = Brush.BrushUnits.USER_SPACE_ON_USE; + break; + } + invalidate(); + } + + @ReactProp(name = "patternContentUnits") + public void setPatternContentUnits(int patternContentUnits) { + switch (patternContentUnits) { + case 0: + mPatternContentUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX; + break; + case 1: + mPatternContentUnits = Brush.BrushUnits.USER_SPACE_ON_USE; + break; + } + invalidate(); + } + + @ReactProp(name = "patternTransform") + public void setPatternTransform(@Nullable ReadableArray matrixArray) { + if (matrixArray != null) { + int matrixSize = PropHelper.toMatrixData(matrixArray, sRawMatrix, mScale); + if (matrixSize == 6) { + if (mMatrix == null) { + mMatrix = new Matrix(); } - invalidate(); + mMatrix.setValues(sRawMatrix); + } else if (matrixSize != -1) { + FLog.w(ReactConstants.TAG, "RNSVG: Transform matrices must be of size 6"); + } + } else { + mMatrix = null; } - @ReactProp(name = "patternContentUnits") - public void setPatternContentUnits(int patternContentUnits) { - switch (patternContentUnits) { - case 0: - mPatternContentUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX; - break; - case 1: - mPatternContentUnits = Brush.BrushUnits.USER_SPACE_ON_USE; - break; - } - invalidate(); - } - - @ReactProp(name = "patternTransform") - public void setPatternTransform(@Nullable ReadableArray matrixArray) { - if (matrixArray != null) { - int matrixSize = PropHelper.toMatrixData(matrixArray, sRawMatrix, mScale); - if (matrixSize == 6) { - if (mMatrix == null) { - mMatrix = new Matrix(); - } - mMatrix.setValues(sRawMatrix); - } else if (matrixSize != -1) { - FLog.w(ReactConstants.TAG, "RNSVG: Transform matrices must be of size 6"); - } - } else { - mMatrix = null; - } - - invalidate(); - } - - @ReactProp(name = "minX") - public void setMinX(float minX) { - mMinX = minX; - invalidate(); - } - - @ReactProp(name = "minY") - public void setMinY(float minY) { - mMinY = minY; - invalidate(); - } - - @ReactProp(name = "vbWidth") - public void setVbWidth(float vbWidth) { - mVbWidth = vbWidth; - invalidate(); - } - - @ReactProp(name = "vbHeight") - public void setVbHeight(float vbHeight) { - mVbHeight = vbHeight; - invalidate(); - } - - @ReactProp(name = "align") - public void setAlign(String align) { - mAlign = align; - invalidate(); - } - - @ReactProp(name = "meetOrSlice") - public void setMeetOrSlice(int meetOrSlice) { - mMeetOrSlice = meetOrSlice; - invalidate(); - } - - - RectF getViewBox() { - return new RectF(mMinX * mScale, mMinY * mScale, (mMinX + mVbWidth) * mScale, (mMinY + mVbHeight) * mScale); - } - - @Override - void saveDefinition() { - if (mName != null) { - SVGLength[] points = new SVGLength[]{mX,mY,mW,mH}; - Brush brush = new Brush(Brush.BrushType.PATTERN, points, mPatternUnits); - brush.setContentUnits(mPatternContentUnits); - brush.setPattern(this); - - if (mMatrix != null) { - brush.setGradientTransform(mMatrix); - } - - SvgView svg = getSvgView(); - if (mPatternUnits == Brush.BrushUnits.USER_SPACE_ON_USE || mPatternContentUnits == Brush.BrushUnits.USER_SPACE_ON_USE) { - brush.setUserSpaceBoundingBox(svg.getCanvasBounds()); - } - - svg.defineBrush(brush, mName); - } + invalidate(); + } + + @ReactProp(name = "minX") + public void setMinX(float minX) { + mMinX = minX; + invalidate(); + } + + @ReactProp(name = "minY") + public void setMinY(float minY) { + mMinY = minY; + invalidate(); + } + + @ReactProp(name = "vbWidth") + public void setVbWidth(float vbWidth) { + mVbWidth = vbWidth; + invalidate(); + } + + @ReactProp(name = "vbHeight") + public void setVbHeight(float vbHeight) { + mVbHeight = vbHeight; + invalidate(); + } + + @ReactProp(name = "align") + public void setAlign(String align) { + mAlign = align; + invalidate(); + } + + @ReactProp(name = "meetOrSlice") + public void setMeetOrSlice(int meetOrSlice) { + mMeetOrSlice = meetOrSlice; + invalidate(); + } + + RectF getViewBox() { + return new RectF( + mMinX * mScale, mMinY * mScale, (mMinX + mVbWidth) * mScale, (mMinY + mVbHeight) * mScale); + } + + @Override + void saveDefinition() { + if (mName != null) { + SVGLength[] points = new SVGLength[] {mX, mY, mW, mH}; + Brush brush = new Brush(Brush.BrushType.PATTERN, points, mPatternUnits); + brush.setContentUnits(mPatternContentUnits); + brush.setPattern(this); + + if (mMatrix != null) { + brush.setGradientTransform(mMatrix); + } + + SvgView svg = getSvgView(); + if (mPatternUnits == Brush.BrushUnits.USER_SPACE_ON_USE + || mPatternContentUnits == Brush.BrushUnits.USER_SPACE_ON_USE) { + brush.setUserSpaceBoundingBox(svg.getCanvasBounds()); + } + + svg.defineBrush(brush, mName); } + } } diff --git a/android/src/main/java/com/horcrux/svg/PropHelper.java b/android/src/main/java/com/horcrux/svg/PropHelper.java index 3b4edab6..c56e98e9 100644 --- a/android/src/main/java/com/horcrux/svg/PropHelper.java +++ b/android/src/main/java/com/horcrux/svg/PropHelper.java @@ -6,215 +6,211 @@ * LICENSE file in the root directory of this source tree. */ - package com.horcrux.svg; import com.facebook.react.bridge.ReadableArray; -/** - * Contains static helper methods for accessing props. - */ +/** Contains static helper methods for accessing props. */ class PropHelper { - private static final int inputMatrixDataSize = 6; + private static final int inputMatrixDataSize = 6; - /** - * Converts given {@link ReadableArray} to a matrix data array, {@code float[6]}. - * Writes result to the array passed in {@param into}. - * This method will write exactly six items to the output array from the input array. - * - * If the input array has a different size, then only the size is returned; - * Does not check output array size. Ensure space for at least six elements. - * - * @param value input array - * @param sRawMatrix output matrix - * @param mScale current resolution scaling - * @return size of input array - */ - static int toMatrixData(ReadableArray value, float[] sRawMatrix, float mScale) { - int fromSize = value.size(); - if (fromSize != inputMatrixDataSize) { - return fromSize; - } - - sRawMatrix[0] = (float) value.getDouble(0); - sRawMatrix[1] = (float) value.getDouble(2); - sRawMatrix[2] = (float) value.getDouble(4) * mScale; - sRawMatrix[3] = (float) value.getDouble(1); - sRawMatrix[4] = (float) value.getDouble(3); - sRawMatrix[5] = (float) value.getDouble(5) * mScale; - - return inputMatrixDataSize; + /** + * Converts given {@link ReadableArray} to a matrix data array, {@code float[6]}. Writes result to + * the array passed in {@param into}. This method will write exactly six items to the output array + * from the input array. + * + *

If the input array has a different size, then only the size is returned; Does not check + * output array size. Ensure space for at least six elements. + * + * @param value input array + * @param sRawMatrix output matrix + * @param mScale current resolution scaling + * @return size of input array + */ + static int toMatrixData(ReadableArray value, float[] sRawMatrix, float mScale) { + int fromSize = value.size(); + if (fromSize != inputMatrixDataSize) { + return fromSize; } - /** - * Converts length string into px / user units - * in the current user coordinate system - * - * @param length length string - * @param relative relative size for percentages - * @param scale scaling parameter - * @param fontSize current font size - * @return value in the current user coordinate system - */ - static double fromRelative(String length, double relative, double scale, double fontSize) { - /* - TODO list + sRawMatrix[0] = (float) value.getDouble(0); + sRawMatrix[1] = (float) value.getDouble(2); + sRawMatrix[2] = (float) value.getDouble(4) * mScale; + sRawMatrix[3] = (float) value.getDouble(1); + sRawMatrix[4] = (float) value.getDouble(3); + sRawMatrix[5] = (float) value.getDouble(5) * mScale; - unit relative to - em font size of the element - ex x-height of the element’s font - ch width of the "0" (ZERO, U+0030) glyph in the element’s font - rem font size of the root element - vw 1% of viewport’s width - vh 1% of viewport’s height - vmin 1% of viewport’s smaller dimension - vmax 1% of viewport’s larger dimension + return inputMatrixDataSize; + } - relative-size [ larger | smaller ] - absolute-size: [ xx-small | x-small | small | medium | large | x-large | xx-large ] + /** + * Converts length string into px / user units in the current user coordinate system + * + * @param length length string + * @param relative relative size for percentages + * @param scale scaling parameter + * @param fontSize current font size + * @return value in the current user coordinate system + */ + static double fromRelative(String length, double relative, double scale, double fontSize) { + /* + TODO list - https://www.w3.org/TR/css3-values/#relative-lengths - https://www.w3.org/TR/css3-values/#absolute-lengths - https://drafts.csswg.org/css-cascade-4/#computed-value - https://drafts.csswg.org/css-fonts-3/#propdef-font-size - https://drafts.csswg.org/css2/fonts.html#propdef-font-size - */ - length = length.trim(); - int stringLength = length.length(); - int percentIndex = stringLength - 1; - if (stringLength == 0 || length.equals("normal")) { - return 0d; - } else if (length.codePointAt(percentIndex) == '%') { - return Double.valueOf(length.substring(0, percentIndex)) / 100 * relative; - } else { - int twoLetterUnitIndex = stringLength - 2; - if (twoLetterUnitIndex > 0) { - String lastTwo = length.substring(twoLetterUnitIndex); - int end = twoLetterUnitIndex; - double unit = 1; + unit relative to + em font size of the element + ex x-height of the element’s font + ch width of the "0" (ZERO, U+0030) glyph in the element’s font + rem font size of the root element + vw 1% of viewport’s width + vh 1% of viewport’s height + vmin 1% of viewport’s smaller dimension + vmax 1% of viewport’s larger dimension - switch (lastTwo) { - case "px": - break; + relative-size [ larger | smaller ] + absolute-size: [ xx-small | x-small | small | medium | large | x-large | xx-large ] - case "em": - unit = fontSize; - break; - - /* - "1pt" equals "1.25px" (and therefore 1.25 user units) - "1pc" equals "15px" (and therefore 15 user units) - "1mm" would be "3.543307px" (3.543307 user units) - "1cm" equals "35.43307px" (and therefore 35.43307 user units) - "1in" equals "90px" (and therefore 90 user units) - */ - - case "pt": - unit = 1.25d; - break; - - case "pc": - unit = 15; - break; - - case "mm": - unit = 3.543307d; - break; - - case "cm": - unit = 35.43307d; - break; - - case "in": - unit = 90; - break; - - default: - end = stringLength; - } - - return Double.valueOf(length.substring(0, end)) * unit * scale; - } else { - return Double.valueOf(length) * scale; - } - } - } - /** - * Converts SVGLength into px / user units - * in the current user coordinate system - * - * @param length length string - * @param relative relative size for percentages - * @param offset offset for all units - * @param scale scaling parameter - * @param fontSize current font size - * @return value in the current user coordinate system - */ - static double fromRelative(SVGLength length, double relative, double offset, double scale, double fontSize) { - /* - TODO list - - unit relative to - em font size of the element - ex x-height of the element’s font - ch width of the "0" (ZERO, U+0030) glyph in the element’s font - rem font size of the root element - vw 1% of viewport’s width - vh 1% of viewport’s height - vmin 1% of viewport’s smaller dimension - vmax 1% of viewport’s larger dimension - - relative-size [ larger | smaller ] - absolute-size: [ xx-small | x-small | small | medium | large | x-large | xx-large ] - - https://www.w3.org/TR/css3-values/#relative-lengths - https://www.w3.org/TR/css3-values/#absolute-lengths - https://drafts.csswg.org/css-cascade-4/#computed-value - https://drafts.csswg.org/css-fonts-3/#propdef-font-size - https://drafts.csswg.org/css2/fonts.html#propdef-font-size - */ - if (length == null) { - return offset; - } - SVGLength.UnitType unitType = length.unit; - double value = length.value; + https://www.w3.org/TR/css3-values/#relative-lengths + https://www.w3.org/TR/css3-values/#absolute-lengths + https://drafts.csswg.org/css-cascade-4/#computed-value + https://drafts.csswg.org/css-fonts-3/#propdef-font-size + https://drafts.csswg.org/css2/fonts.html#propdef-font-size + */ + length = length.trim(); + int stringLength = length.length(); + int percentIndex = stringLength - 1; + if (stringLength == 0 || length.equals("normal")) { + return 0d; + } else if (length.codePointAt(percentIndex) == '%') { + return Double.valueOf(length.substring(0, percentIndex)) / 100 * relative; + } else { + int twoLetterUnitIndex = stringLength - 2; + if (twoLetterUnitIndex > 0) { + String lastTwo = length.substring(twoLetterUnitIndex); + int end = twoLetterUnitIndex; double unit = 1; - switch (unitType) { - case NUMBER: - case PX: - break; - case PERCENTAGE: - return value / 100 * relative + offset; + switch (lastTwo) { + case "px": + break; - case EMS: - unit = fontSize; - break; - case EXS: - unit = fontSize / 2; - break; + case "em": + unit = fontSize; + break; - case CM: - unit = 35.43307; - break; - case MM: - unit = 3.543307; - break; - case IN: - unit = 90; - break; - case PT: - unit = 1.25; - break; - case PC: - unit = 15; - break; + /* + "1pt" equals "1.25px" (and therefore 1.25 user units) + "1pc" equals "15px" (and therefore 15 user units) + "1mm" would be "3.543307px" (3.543307 user units) + "1cm" equals "35.43307px" (and therefore 35.43307 user units) + "1in" equals "90px" (and therefore 90 user units) + */ - default: - case UNKNOWN: - return value * scale + offset; + case "pt": + unit = 1.25d; + break; + + case "pc": + unit = 15; + break; + + case "mm": + unit = 3.543307d; + break; + + case "cm": + unit = 35.43307d; + break; + + case "in": + unit = 90; + break; + + default: + end = stringLength; } - return value * unit * scale + offset; + + return Double.valueOf(length.substring(0, end)) * unit * scale; + } else { + return Double.valueOf(length) * scale; + } } + } + /** + * Converts SVGLength into px / user units in the current user coordinate system + * + * @param length length string + * @param relative relative size for percentages + * @param offset offset for all units + * @param scale scaling parameter + * @param fontSize current font size + * @return value in the current user coordinate system + */ + static double fromRelative( + SVGLength length, double relative, double offset, double scale, double fontSize) { + /* + TODO list + + unit relative to + em font size of the element + ex x-height of the element’s font + ch width of the "0" (ZERO, U+0030) glyph in the element’s font + rem font size of the root element + vw 1% of viewport’s width + vh 1% of viewport’s height + vmin 1% of viewport’s smaller dimension + vmax 1% of viewport’s larger dimension + + relative-size [ larger | smaller ] + absolute-size: [ xx-small | x-small | small | medium | large | x-large | xx-large ] + + https://www.w3.org/TR/css3-values/#relative-lengths + https://www.w3.org/TR/css3-values/#absolute-lengths + https://drafts.csswg.org/css-cascade-4/#computed-value + https://drafts.csswg.org/css-fonts-3/#propdef-font-size + https://drafts.csswg.org/css2/fonts.html#propdef-font-size + */ + if (length == null) { + return offset; + } + SVGLength.UnitType unitType = length.unit; + double value = length.value; + double unit = 1; + switch (unitType) { + case NUMBER: + case PX: + break; + + case PERCENTAGE: + return value / 100 * relative + offset; + + case EMS: + unit = fontSize; + break; + case EXS: + unit = fontSize / 2; + break; + + case CM: + unit = 35.43307; + break; + case MM: + unit = 3.543307; + break; + case IN: + unit = 90; + break; + case PT: + unit = 1.25; + break; + case PC: + unit = 15; + break; + + default: + case UNKNOWN: + return value * scale + offset; + } + return value * unit * scale + offset; + } } diff --git a/android/src/main/java/com/horcrux/svg/RNSVGMarkerPosition.java b/android/src/main/java/com/horcrux/svg/RNSVGMarkerPosition.java index 66cf1276..83ae08cd 100644 --- a/android/src/main/java/com/horcrux/svg/RNSVGMarkerPosition.java +++ b/android/src/main/java/com/horcrux/svg/RNSVGMarkerPosition.java @@ -3,178 +3,170 @@ package com.horcrux.svg; import java.util.ArrayList; enum RNSVGMarkerType { - kStartMarker, - kMidMarker, - kEndMarker + kStartMarker, + kMidMarker, + kEndMarker } enum ElementType { - kCGPathElementAddCurveToPoint, - kCGPathElementAddQuadCurveToPoint, - kCGPathElementMoveToPoint, - kCGPathElementAddLineToPoint, - kCGPathElementCloseSubpath + kCGPathElementAddCurveToPoint, + kCGPathElementAddQuadCurveToPoint, + kCGPathElementMoveToPoint, + kCGPathElementAddLineToPoint, + kCGPathElementCloseSubpath } class Point { - double x; - double y; - Point(double x, double y){ - this.x = x; - this.y = y; - } + double x; + double y; + + Point(double x, double y) { + this.x = x; + this.y = y; + } } class SegmentData { - Point start_tangent; // Tangent in the start point of the segment. - Point end_tangent; // Tangent in the end point of the segment. - Point position; // The end point of the segment. + Point start_tangent; // Tangent in the start point of the segment. + Point end_tangent; // Tangent in the end point of the segment. + Point position; // The end point of the segment. } class RNSVGMarkerPosition { - static private ArrayList positions_; - static private int element_index_; - static private Point origin_; - static private Point subpath_start_; - static private Point in_slope_; - static private Point out_slope_; - @SuppressWarnings("unused") - static private boolean auto_start_reverse_; // TODO + private static ArrayList positions_; + private static int element_index_; + private static Point origin_; + private static Point subpath_start_; + private static Point in_slope_; + private static Point out_slope_; - RNSVGMarkerType type; - Point origin; - double angle; + @SuppressWarnings("unused") + private static boolean auto_start_reverse_; // TODO - private RNSVGMarkerPosition(RNSVGMarkerType type, Point origin, double angle) { - this.type = type; - this.origin = origin; - this.angle = angle; + RNSVGMarkerType type; + Point origin; + double angle; + + private RNSVGMarkerPosition(RNSVGMarkerType type, Point origin, double angle) { + this.type = type; + this.origin = origin; + this.angle = angle; + } + + static ArrayList fromPath(ArrayList elements) { + positions_ = new ArrayList<>(); + element_index_ = 0; + origin_ = new Point(0, 0); + subpath_start_ = new Point(0, 0); + for (PathElement e : elements) { + UpdateFromPathElement(e); } + PathIsDone(); + return positions_; + } - static ArrayList fromPath(ArrayList elements) { - positions_ = new ArrayList<>(); - element_index_ = 0; - origin_ = new Point(0, 0); - subpath_start_ = new Point(0, 0); - for (PathElement e : elements) { - UpdateFromPathElement(e); - } - PathIsDone(); - return positions_; - } + private static void PathIsDone() { + double angle = CurrentAngle(RNSVGMarkerType.kEndMarker); + positions_.add(new RNSVGMarkerPosition(RNSVGMarkerType.kEndMarker, origin_, angle)); + } - private static void PathIsDone() { - double angle = CurrentAngle(RNSVGMarkerType.kEndMarker); - positions_.add(new RNSVGMarkerPosition(RNSVGMarkerType.kEndMarker, origin_, angle)); - } + private static double BisectingAngle(double in_angle, double out_angle) { + // WK193015: Prevent bugs due to angles being non-continuous. + if (Math.abs(in_angle - out_angle) > 180) in_angle += 360; + return (in_angle + out_angle) / 2; + } - private static double BisectingAngle(double in_angle, double out_angle) { - // WK193015: Prevent bugs due to angles being non-continuous. - if (Math.abs(in_angle - out_angle) > 180) - in_angle += 360; - return (in_angle + out_angle) / 2; - } + private static double rad2deg(double rad) { + double RNSVG_radToDeg = 180 / Math.PI; + return rad * RNSVG_radToDeg; + } - private static double rad2deg(double rad) { - double RNSVG_radToDeg = 180 / Math.PI; - return rad * RNSVG_radToDeg; - } + private static double SlopeAngleRadians(Point p) { + return Math.atan2(p.y, p.x); + } - private static double SlopeAngleRadians(Point p) { - return Math.atan2(p.y, p.x); + private static double CurrentAngle(RNSVGMarkerType type) { + // For details of this calculation, see: + // http://www.w3.org/TR/SVG/single-page.html#painting-MarkerElement + double in_angle = rad2deg(SlopeAngleRadians(in_slope_)); + double out_angle = rad2deg(SlopeAngleRadians(out_slope_)); + switch (type) { + case kStartMarker: + if (auto_start_reverse_) out_angle += 180; + return out_angle; + case kMidMarker: + return BisectingAngle(in_angle, out_angle); + case kEndMarker: + return in_angle; } + return 0; + } - private static double CurrentAngle(RNSVGMarkerType type) { - // For details of this calculation, see: - // http://www.w3.org/TR/SVG/single-page.html#painting-MarkerElement - double in_angle = rad2deg(SlopeAngleRadians(in_slope_)); - double out_angle = rad2deg(SlopeAngleRadians(out_slope_)); - switch (type) { - case kStartMarker: - if (auto_start_reverse_) - out_angle += 180; - return out_angle; - case kMidMarker: - return BisectingAngle(in_angle, out_angle); - case kEndMarker: - return in_angle; - } - return 0; - } + private static Point subtract(Point p1, Point p2) { + return new Point(p2.x - p1.x, p2.y - p1.y); + } - private static Point subtract(Point p1, Point p2) { - return new Point(p2.x - p1.x, p2.y - p1.y); - } + private static boolean isZero(Point p) { + return p.x == 0 && p.y == 0; + } - private static boolean isZero(Point p) { - return p.x == 0 && p.y == 0; - } + private static void ComputeQuadTangents(SegmentData data, Point start, Point control, Point end) { + data.start_tangent = subtract(control, start); + data.end_tangent = subtract(end, control); + if (isZero(data.start_tangent)) data.start_tangent = data.end_tangent; + else if (isZero(data.end_tangent)) data.end_tangent = data.start_tangent; + } - private static void ComputeQuadTangents(SegmentData data, - Point start, - Point control, - Point end) { - data.start_tangent = subtract(control, start); - data.end_tangent = subtract(end, control); - if (isZero(data.start_tangent)) - data.start_tangent = data.end_tangent; - else if (isZero(data.end_tangent)) - data.end_tangent = data.start_tangent; + private static SegmentData ExtractPathElementFeatures(PathElement element) { + SegmentData data = new SegmentData(); + Point[] points = element.points; + switch (element.type) { + case kCGPathElementAddCurveToPoint: + data.position = points[2]; + data.start_tangent = subtract(points[0], origin_); + data.end_tangent = subtract(points[2], points[1]); + if (isZero(data.start_tangent)) ComputeQuadTangents(data, points[0], points[1], points[2]); + else if (isZero(data.end_tangent)) ComputeQuadTangents(data, origin_, points[0], points[1]); + break; + case kCGPathElementAddQuadCurveToPoint: + data.position = points[1]; + ComputeQuadTangents(data, origin_, points[0], points[1]); + break; + case kCGPathElementMoveToPoint: + case kCGPathElementAddLineToPoint: + data.position = points[0]; + data.start_tangent = subtract(data.position, origin_); + data.end_tangent = subtract(data.position, origin_); + break; + case kCGPathElementCloseSubpath: + data.position = subpath_start_; + data.start_tangent = subtract(data.position, origin_); + data.end_tangent = subtract(data.position, origin_); + break; } + return data; + } - private static SegmentData ExtractPathElementFeatures(PathElement element) { - SegmentData data = new SegmentData(); - Point[] points = element.points; - switch (element.type) { - case kCGPathElementAddCurveToPoint: - data.position = points[2]; - data.start_tangent = subtract(points[0], origin_); - data.end_tangent = subtract(points[2], points[1]); - if (isZero(data.start_tangent)) - ComputeQuadTangents(data, points[0], points[1], points[2]); - else if (isZero(data.end_tangent)) - ComputeQuadTangents(data, origin_, points[0], points[1]); - break; - case kCGPathElementAddQuadCurveToPoint: - data.position = points[1]; - ComputeQuadTangents(data, origin_, points[0], points[1]); - break; - case kCGPathElementMoveToPoint: - case kCGPathElementAddLineToPoint: - data.position = points[0]; - data.start_tangent = subtract(data.position, origin_); - data.end_tangent = subtract(data.position, origin_); - break; - case kCGPathElementCloseSubpath: - data.position = subpath_start_; - data.start_tangent = subtract(data.position, origin_); - data.end_tangent = subtract(data.position, origin_); - break; - } - return data; - } - - private static void UpdateFromPathElement(PathElement element) { - SegmentData segment_data = ExtractPathElementFeatures(element); - // First update the outgoing slope for the previous element. - out_slope_ = segment_data.start_tangent; - // Record the marker for the previous element. - if (element_index_ > 0) { - RNSVGMarkerType marker_type = - element_index_ == 1 ? RNSVGMarkerType.kStartMarker : RNSVGMarkerType.kMidMarker; - double angle = CurrentAngle(marker_type); - positions_.add(new RNSVGMarkerPosition(marker_type, origin_, angle)); - } - // Update the incoming slope for this marker position. - in_slope_ = segment_data.end_tangent; - // Update marker position. - origin_ = segment_data.position; - // If this is a 'move to' segment, save the point for use with 'close'. - if (element.type == ElementType.kCGPathElementMoveToPoint) - subpath_start_ = element.points[0]; - else if (element.type == ElementType.kCGPathElementCloseSubpath) - subpath_start_ = new Point(0, 0); - ++element_index_; + private static void UpdateFromPathElement(PathElement element) { + SegmentData segment_data = ExtractPathElementFeatures(element); + // First update the outgoing slope for the previous element. + out_slope_ = segment_data.start_tangent; + // Record the marker for the previous element. + if (element_index_ > 0) { + RNSVGMarkerType marker_type = + element_index_ == 1 ? RNSVGMarkerType.kStartMarker : RNSVGMarkerType.kMidMarker; + double angle = CurrentAngle(marker_type); + positions_.add(new RNSVGMarkerPosition(marker_type, origin_, angle)); } + // Update the incoming slope for this marker position. + in_slope_ = segment_data.end_tangent; + // Update marker position. + origin_ = segment_data.position; + // If this is a 'move to' segment, save the point for use with 'close'. + if (element.type == ElementType.kCGPathElementMoveToPoint) subpath_start_ = element.points[0]; + else if (element.type == ElementType.kCGPathElementCloseSubpath) + subpath_start_ = new Point(0, 0); + ++element_index_; + } } diff --git a/android/src/main/java/com/horcrux/svg/RNSVGRenderableManager.java b/android/src/main/java/com/horcrux/svg/RNSVGRenderableManager.java index 71856fd0..1d08408d 100644 --- a/android/src/main/java/com/horcrux/svg/RNSVGRenderableManager.java +++ b/android/src/main/java/com/horcrux/svg/RNSVGRenderableManager.java @@ -6,16 +6,16 @@ * LICENSE file in the root directory of this source tree. */ - package com.horcrux.svg; +import static com.facebook.react.common.StandardCharsets.UTF_8; + import android.content.res.Resources; import android.graphics.Matrix; import android.graphics.Path; import android.graphics.PathMeasure; import android.graphics.RectF; import android.graphics.Region; - import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.ReactApplicationContext; @@ -23,251 +23,247 @@ import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.WritableMap; - import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; - import javax.annotation.Nonnull; -import static com.facebook.react.common.StandardCharsets.UTF_8; - class RNSVGRenderableManager extends ReactContextBaseJavaModule { - RNSVGRenderableManager(ReactApplicationContext reactContext) { - super(reactContext); + RNSVGRenderableManager(ReactApplicationContext reactContext) { + super(reactContext); + } + + @Nonnull + @Override + public String getName() { + return "RNSVGRenderableManager"; + } + + @SuppressWarnings("unused") + @ReactMethod(isBlockingSynchronousMethod = true) + public boolean isPointInFill(int tag, ReadableMap options) { + RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag); + if (svg == null) { + return false; } - @Nonnull - @Override - public String getName() { - return "RNSVGRenderableManager"; + float scale = svg.mScale; + float x = (float) options.getDouble("x") * scale; + float y = (float) options.getDouble("y") * scale; + + int i = svg.hitTest(new float[] {x, y}); + return i != -1; + } + + @SuppressWarnings("unused") + @ReactMethod(isBlockingSynchronousMethod = true) + public boolean isPointInStroke(int tag, ReadableMap options) { + RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag); + if (svg == null) { + return false; } - @SuppressWarnings("unused") - @ReactMethod(isBlockingSynchronousMethod = true) - public boolean isPointInFill(int tag, ReadableMap options) { - RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag); - if (svg == null) { - return false; + try { + svg.getPath(null, null); + } catch (NullPointerException e) { + svg.invalidate(); + return false; + } + + svg.initBounds(); + + float scale = svg.mScale; + int x = (int) (options.getDouble("x") * scale); + int y = (int) (options.getDouble("y") * scale); + + Region strokeRegion = svg.mStrokeRegion; + return strokeRegion != null && strokeRegion.contains(x, y); + } + + @SuppressWarnings("unused") + @ReactMethod(isBlockingSynchronousMethod = true) + public float getTotalLength(int tag) { + RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag); + if (svg == null) { + return 0; + } + + Path path; + + try { + path = svg.getPath(null, null); + } catch (NullPointerException e) { + svg.invalidate(); + return -1; + } + + PathMeasure pm = new PathMeasure(path, false); + return pm.getLength() / svg.mScale; + } + + @SuppressWarnings("unused") + @ReactMethod(isBlockingSynchronousMethod = true) + public WritableMap getPointAtLength(int tag, ReadableMap options) { + RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag); + if (svg == null) { + return Arguments.createMap(); + } + + Path path; + + try { + path = svg.getPath(null, null); + } catch (NullPointerException e) { + svg.invalidate(); + return Arguments.createMap(); + } + + PathMeasure pm = new PathMeasure(path, false); + float length = (float) options.getDouble("length"); + float scale = svg.mScale; + + float[] pos = new float[2]; + float[] tan = new float[2]; + float distance = Math.max(0, Math.min(length, pm.getLength())); + pm.getPosTan(distance, pos, tan); + + double angle = Math.atan2(tan[1], tan[0]); + WritableMap result = Arguments.createMap(); + result.putDouble("x", pos[0] / scale); + result.putDouble("y", pos[1] / scale); + result.putDouble("angle", angle); + return result; + } + + @SuppressWarnings("unused") + @ReactMethod(isBlockingSynchronousMethod = true) + public WritableMap getBBox(int tag, ReadableMap options) { + RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag); + if (svg == null) { + return Arguments.createMap(); + } + + boolean fill = options.getBoolean("fill"); + boolean stroke = options.getBoolean("stroke"); + boolean markers = options.getBoolean("markers"); + boolean clipped = options.getBoolean("clipped"); + + try { + svg.getPath(null, null); + } catch (NullPointerException e) { + svg.invalidate(); + return Arguments.createMap(); + } + + float scale = svg.mScale; + svg.initBounds(); + + RectF bounds = new RectF(); + RectF fillBounds = svg.mFillBounds; + RectF strokeBounds = svg.mStrokeBounds; + RectF markerBounds = svg.mMarkerBounds; + RectF clipBounds = svg.mClipBounds; + + if (fill && fillBounds != null) { + bounds.union(fillBounds); + } + if (stroke && strokeBounds != null) { + bounds.union(strokeBounds); + } + if (markers && markerBounds != null) { + bounds.union(markerBounds); + } + if (clipped && clipBounds != null) { + bounds.intersect(clipBounds); + } + + WritableMap result = Arguments.createMap(); + result.putDouble("x", bounds.left / scale); + result.putDouble("y", bounds.top / scale); + result.putDouble("width", bounds.width() / scale); + result.putDouble("height", bounds.height() / scale); + return result; + } + + @SuppressWarnings("unused") + @ReactMethod(isBlockingSynchronousMethod = true) + public WritableMap getCTM(int tag) { + RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag); + if (svg == null) { + return Arguments.createMap(); + } + + float scale = svg.mScale; + Matrix ctm = new Matrix(svg.mCTM); + Matrix invViewBoxMatrix = svg.getSvgView().mInvViewBoxMatrix; + ctm.preConcat(invViewBoxMatrix); + + float[] values = new float[9]; + ctm.getValues(values); + + WritableMap result = Arguments.createMap(); + result.putDouble("a", values[Matrix.MSCALE_X]); + result.putDouble("b", values[Matrix.MSKEW_Y]); + result.putDouble("c", values[Matrix.MSKEW_X]); + result.putDouble("d", values[Matrix.MSCALE_Y]); + result.putDouble("e", values[Matrix.MTRANS_X] / scale); + result.putDouble("f", values[Matrix.MTRANS_Y] / scale); + return result; + } + + @SuppressWarnings("unused") + @ReactMethod(isBlockingSynchronousMethod = true) + public WritableMap getScreenCTM(int tag) { + RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag); + if (svg == null) { + return Arguments.createMap(); + } + + float[] values = new float[9]; + svg.mCTM.getValues(values); + float scale = svg.mScale; + + WritableMap result = Arguments.createMap(); + result.putDouble("a", values[Matrix.MSCALE_X]); + result.putDouble("b", values[Matrix.MSKEW_Y]); + result.putDouble("c", values[Matrix.MSKEW_X]); + result.putDouble("d", values[Matrix.MSCALE_Y]); + result.putDouble("e", values[Matrix.MTRANS_X] / scale); + result.putDouble("f", values[Matrix.MTRANS_Y] / scale); + return result; + } + + @ReactMethod + public void getRawResource(String name, Promise promise) { + try { + ReactApplicationContext context = getReactApplicationContext(); + Resources resources = context.getResources(); + String packageName = context.getPackageName(); + int id = resources.getIdentifier(name, "raw", packageName); + InputStream stream = resources.openRawResource(id); + try { + InputStreamReader reader = new InputStreamReader(stream, UTF_8); + char[] buffer = new char[DEFAULT_BUFFER_SIZE]; + StringBuilder builder = new StringBuilder(); + int n; + while ((n = reader.read(buffer, 0, DEFAULT_BUFFER_SIZE)) != EOF) { + builder.append(buffer, 0, n); } - - float scale = svg.mScale; - float x = (float) options.getDouble("x") * scale; - float y = (float) options.getDouble("y") * scale; - - int i = svg.hitTest(new float[]{x, y}); - return i != -1; - } - - @SuppressWarnings("unused") - @ReactMethod(isBlockingSynchronousMethod = true) - public boolean isPointInStroke(int tag, ReadableMap options) { - RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag); - if (svg == null) { - return false; - } - + String result = builder.toString(); + promise.resolve(result); + } finally { try { - svg.getPath(null, null); - } catch (NullPointerException e) { - svg.invalidate(); - return false; + stream.close(); + } catch (IOException ioe) { + // ignore } - - svg.initBounds(); - - float scale = svg.mScale; - int x = (int) (options.getDouble("x") * scale); - int y = (int) (options.getDouble("y") * scale); - - Region strokeRegion = svg.mStrokeRegion; - return strokeRegion != null && strokeRegion.contains(x, y); + } + } catch (Exception e) { + e.printStackTrace(); + promise.reject(e); } + } - @SuppressWarnings("unused") - @ReactMethod(isBlockingSynchronousMethod = true) - public float getTotalLength(int tag) { - RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag); - if (svg == null) { - return 0; - } - - Path path; - - try { - path = svg.getPath(null, null); - } catch (NullPointerException e) { - svg.invalidate(); - return -1; - } - - PathMeasure pm = new PathMeasure(path, false); - return pm.getLength() / svg.mScale; - } - - @SuppressWarnings("unused") - @ReactMethod(isBlockingSynchronousMethod = true) - public WritableMap getPointAtLength(int tag, ReadableMap options) { - RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag); - if (svg == null) { - return Arguments.createMap(); - } - - Path path; - - try { - path = svg.getPath(null, null); - } catch (NullPointerException e) { - svg.invalidate(); - return Arguments.createMap(); - } - - PathMeasure pm = new PathMeasure(path, false); - float length = (float) options.getDouble("length"); - float scale = svg.mScale; - - float[] pos = new float[2]; - float[] tan = new float[2]; - float distance = Math.max(0, Math.min(length, pm.getLength())); - pm.getPosTan(distance, pos, tan); - - double angle = Math.atan2(tan[1], tan[0]); - WritableMap result = Arguments.createMap(); - result.putDouble("x", pos[0] / scale); - result.putDouble("y", pos[1] / scale); - result.putDouble("angle", angle); - return result; - } - - @SuppressWarnings("unused") - @ReactMethod(isBlockingSynchronousMethod = true) - public WritableMap getBBox(int tag, ReadableMap options) { - RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag); - if (svg == null) { - return Arguments.createMap(); - } - - boolean fill = options.getBoolean("fill"); - boolean stroke = options.getBoolean("stroke"); - boolean markers = options.getBoolean("markers"); - boolean clipped = options.getBoolean("clipped"); - - try { - svg.getPath(null, null); - } catch (NullPointerException e) { - svg.invalidate(); - return Arguments.createMap(); - } - - float scale = svg.mScale; - svg.initBounds(); - - RectF bounds = new RectF(); - RectF fillBounds = svg.mFillBounds; - RectF strokeBounds = svg.mStrokeBounds; - RectF markerBounds = svg.mMarkerBounds; - RectF clipBounds = svg.mClipBounds; - - if (fill && fillBounds != null) { - bounds.union(fillBounds); - } - if (stroke && strokeBounds != null) { - bounds.union(strokeBounds); - } - if (markers && markerBounds != null) { - bounds.union(markerBounds); - } - if (clipped && clipBounds != null) { - bounds.intersect(clipBounds); - } - - WritableMap result = Arguments.createMap(); - result.putDouble("x", bounds.left / scale); - result.putDouble("y", bounds.top / scale); - result.putDouble("width", bounds.width() / scale); - result.putDouble("height", bounds.height() / scale); - return result; - } - - @SuppressWarnings("unused") - @ReactMethod(isBlockingSynchronousMethod = true) - public WritableMap getCTM(int tag) { - RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag); - if (svg == null) { - return Arguments.createMap(); - } - - float scale = svg.mScale; - Matrix ctm = new Matrix(svg.mCTM); - Matrix invViewBoxMatrix = svg.getSvgView().mInvViewBoxMatrix; - ctm.preConcat(invViewBoxMatrix); - - float[] values = new float[9]; - ctm.getValues(values); - - WritableMap result = Arguments.createMap(); - result.putDouble("a", values[Matrix.MSCALE_X]); - result.putDouble("b", values[Matrix.MSKEW_Y]); - result.putDouble("c", values[Matrix.MSKEW_X]); - result.putDouble("d", values[Matrix.MSCALE_Y]); - result.putDouble("e", values[Matrix.MTRANS_X] / scale); - result.putDouble("f", values[Matrix.MTRANS_Y] / scale); - return result; - } - - @SuppressWarnings("unused") - @ReactMethod(isBlockingSynchronousMethod = true) - public WritableMap getScreenCTM(int tag) { - RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag); - if (svg == null) { - return Arguments.createMap(); - } - - float[] values = new float[9]; - svg.mCTM.getValues(values); - float scale = svg.mScale; - - WritableMap result = Arguments.createMap(); - result.putDouble("a", values[Matrix.MSCALE_X]); - result.putDouble("b", values[Matrix.MSKEW_Y]); - result.putDouble("c", values[Matrix.MSKEW_X]); - result.putDouble("d", values[Matrix.MSCALE_Y]); - result.putDouble("e", values[Matrix.MTRANS_X] / scale); - result.putDouble("f", values[Matrix.MTRANS_Y] / scale); - return result; - } - - @ReactMethod - public void getRawResource(String name, Promise promise) { - try { - ReactApplicationContext context = getReactApplicationContext(); - Resources resources = context.getResources(); - String packageName = context.getPackageName(); - int id = resources.getIdentifier(name, "raw", packageName); - InputStream stream = resources.openRawResource(id); - try { - InputStreamReader reader = new InputStreamReader(stream, UTF_8); - char[] buffer = new char[DEFAULT_BUFFER_SIZE]; - StringBuilder builder = new StringBuilder(); - int n; - while ((n = reader.read(buffer, 0, DEFAULT_BUFFER_SIZE)) != EOF) { - builder.append(buffer, 0, n); - } - String result = builder.toString(); - promise.resolve(result); - } finally { - try { - stream.close(); - } catch (IOException ioe) { - // ignore - } - } - } catch (Exception e) { - e.printStackTrace(); - promise.reject(e); - } - } - - private static final int EOF = -1; - private static final int DEFAULT_BUFFER_SIZE = 1024 * 4; + private static final int EOF = -1; + private static final int DEFAULT_BUFFER_SIZE = 1024 * 4; } diff --git a/android/src/main/java/com/horcrux/svg/RadialGradientView.java b/android/src/main/java/com/horcrux/svg/RadialGradientView.java index ac6cbb0a..ae20fa39 100644 --- a/android/src/main/java/com/horcrux/svg/RadialGradientView.java +++ b/android/src/main/java/com/horcrux/svg/RadialGradientView.java @@ -6,163 +6,161 @@ * LICENSE file in the root directory of this source tree. */ - package com.horcrux.svg; import android.annotation.SuppressLint; import android.graphics.Matrix; - import com.facebook.common.logging.FLog; import com.facebook.react.bridge.Dynamic; import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.common.ReactConstants; import com.facebook.react.uimanager.annotations.ReactProp; - import javax.annotation.Nullable; @SuppressLint("ViewConstructor") class RadialGradientView extends DefinitionView { - private SVGLength mFx; - private SVGLength mFy; - private SVGLength mRx; - private SVGLength mRy; - private SVGLength mCx; - private SVGLength mCy; - private ReadableArray mGradient; - private Brush.BrushUnits mGradientUnits; + private SVGLength mFx; + private SVGLength mFy; + private SVGLength mRx; + private SVGLength mRy; + private SVGLength mCx; + private SVGLength mCy; + private ReadableArray mGradient; + private Brush.BrushUnits mGradientUnits; - private static final float[] sRawMatrix = new float[]{ + private static final float[] sRawMatrix = + new float[] { 1, 0, 0, 0, 1, 0, 0, 0, 1 - }; - private Matrix mMatrix = null; + }; + private Matrix mMatrix = null; - public RadialGradientView(ReactContext reactContext) { - super(reactContext); - } + public RadialGradientView(ReactContext reactContext) { + super(reactContext); + } - @ReactProp(name = "fx") - public void setFx(Dynamic fx) { - mFx = SVGLength.from(fx); - invalidate(); - } + @ReactProp(name = "fx") + public void setFx(Dynamic fx) { + mFx = SVGLength.from(fx); + invalidate(); + } public void setFx(String fx) { mFx = SVGLength.from(fx); invalidate(); } - @ReactProp(name = "fy") - public void setFy(Dynamic fy) { - mFy = SVGLength.from(fy); - invalidate(); - } + @ReactProp(name = "fy") + public void setFy(Dynamic fy) { + mFy = SVGLength.from(fy); + invalidate(); + } public void setFy(String fy) { mFy = SVGLength.from(fy); invalidate(); } - @ReactProp(name = "rx") - public void setRx(Dynamic rx) { - mRx = SVGLength.from(rx); - invalidate(); - } + @ReactProp(name = "rx") + public void setRx(Dynamic rx) { + mRx = SVGLength.from(rx); + invalidate(); + } public void setRx(String rx) { mRx = SVGLength.from(rx); invalidate(); } - @ReactProp(name = "ry") - public void setRy(Dynamic ry) { - mRy = SVGLength.from(ry); - invalidate(); - } + @ReactProp(name = "ry") + public void setRy(Dynamic ry) { + mRy = SVGLength.from(ry); + invalidate(); + } public void setRy(String ry) { mRy = SVGLength.from(ry); invalidate(); } - @ReactProp(name = "cx") - public void setCx(Dynamic cx) { - mCx = SVGLength.from(cx); - invalidate(); - } + @ReactProp(name = "cx") + public void setCx(Dynamic cx) { + mCx = SVGLength.from(cx); + invalidate(); + } public void setCx(String cx) { mCx = SVGLength.from(cx); invalidate(); } - @ReactProp(name = "cy") - public void setCy(Dynamic cy) { - mCy = SVGLength.from(cy); - invalidate(); - } + @ReactProp(name = "cy") + public void setCy(Dynamic cy) { + mCy = SVGLength.from(cy); + invalidate(); + } public void setCy(String cy) { mCy = SVGLength.from(cy); invalidate(); } - @ReactProp(name = "gradient") - public void setGradient(ReadableArray gradient) { - mGradient = gradient; - invalidate(); - } + @ReactProp(name = "gradient") + public void setGradient(ReadableArray gradient) { + mGradient = gradient; + invalidate(); + } - @ReactProp(name = "gradientUnits") - public void setGradientUnits(int gradientUnits) { - switch (gradientUnits) { - case 0: - mGradientUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX; - break; - case 1: - mGradientUnits = Brush.BrushUnits.USER_SPACE_ON_USE; - break; + @ReactProp(name = "gradientUnits") + public void setGradientUnits(int gradientUnits) { + switch (gradientUnits) { + case 0: + mGradientUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX; + break; + case 1: + mGradientUnits = Brush.BrushUnits.USER_SPACE_ON_USE; + break; + } + invalidate(); + } + + @ReactProp(name = "gradientTransform") + public void setGradientTransform(@Nullable ReadableArray matrixArray) { + if (matrixArray != null) { + int matrixSize = PropHelper.toMatrixData(matrixArray, sRawMatrix, mScale); + if (matrixSize == 6) { + if (mMatrix == null) { + mMatrix = new Matrix(); } - invalidate(); + mMatrix.setValues(sRawMatrix); + } else if (matrixSize != -1) { + FLog.w(ReactConstants.TAG, "RNSVG: Transform matrices must be of size 6"); + } + } else { + mMatrix = null; } - @ReactProp(name = "gradientTransform") - public void setGradientTransform(@Nullable ReadableArray matrixArray) { - if (matrixArray != null) { - int matrixSize = PropHelper.toMatrixData(matrixArray, sRawMatrix, mScale); - if (matrixSize == 6) { - if (mMatrix == null) { - mMatrix = new Matrix(); - } - mMatrix.setValues(sRawMatrix); - } else if (matrixSize != -1) { - FLog.w(ReactConstants.TAG, "RNSVG: Transform matrices must be of size 6"); - } - } else { - mMatrix = null; - } + invalidate(); + } - invalidate(); - } - - @Override - void saveDefinition() { - if (mName != null) { - SVGLength[] points = new SVGLength[]{mFx,mFy,mRx,mRy,mCx,mCy}; - Brush brush = new Brush(Brush.BrushType.RADIAL_GRADIENT, points, mGradientUnits); - brush.setGradientColors(mGradient); - if (mMatrix != null) { - brush.setGradientTransform(mMatrix); - } - - SvgView svg = getSvgView(); - if (mGradientUnits == Brush.BrushUnits.USER_SPACE_ON_USE) { - brush.setUserSpaceBoundingBox(svg.getCanvasBounds()); - } - - svg.defineBrush(brush, mName); - } + @Override + void saveDefinition() { + if (mName != null) { + SVGLength[] points = new SVGLength[] {mFx, mFy, mRx, mRy, mCx, mCy}; + Brush brush = new Brush(Brush.BrushType.RADIAL_GRADIENT, points, mGradientUnits); + brush.setGradientColors(mGradient); + if (mMatrix != null) { + brush.setGradientTransform(mMatrix); + } + + SvgView svg = getSvgView(); + if (mGradientUnits == Brush.BrushUnits.USER_SPACE_ON_USE) { + brush.setUserSpaceBoundingBox(svg.getCanvasBounds()); + } + + svg.defineBrush(brush, mName); } + } } diff --git a/android/src/main/java/com/horcrux/svg/RectView.java b/android/src/main/java/com/horcrux/svg/RectView.java index 0de262ff..e57169aa 100644 --- a/android/src/main/java/com/horcrux/svg/RectView.java +++ b/android/src/main/java/com/horcrux/svg/RectView.java @@ -6,7 +6,6 @@ * LICENSE file in the root directory of this source tree. */ - package com.horcrux.svg; import android.annotation.SuppressLint; @@ -15,128 +14,139 @@ import android.graphics.Paint; import android.graphics.Path; import android.graphics.RectF; import android.os.Build; - import com.facebook.react.bridge.Dynamic; import com.facebook.react.bridge.ReactContext; import com.facebook.react.uimanager.annotations.ReactProp; @SuppressLint("ViewConstructor") class RectView extends RenderableView { - private SVGLength mX; - private SVGLength mY; - private SVGLength mW; - private SVGLength mH; - private SVGLength mRx; - private SVGLength mRy; + private SVGLength mX; + private SVGLength mY; + private SVGLength mW; + private SVGLength mH; + private SVGLength mRx; + private SVGLength mRy; - public RectView(ReactContext reactContext) { - super(reactContext); - } + public RectView(ReactContext reactContext) { + super(reactContext); + } - @ReactProp(name = "x") - public void setX(Dynamic x) { - mX = SVGLength.from(x); - invalidate(); - } + @ReactProp(name = "x") + public void setX(Dynamic x) { + mX = SVGLength.from(x); + invalidate(); + } public void setX(String x) { mX = SVGLength.from(x); invalidate(); } - @ReactProp(name = "y") - public void setY(Dynamic y) { - mY = SVGLength.from(y); - invalidate(); - } + @ReactProp(name = "y") + public void setY(Dynamic y) { + mY = SVGLength.from(y); + invalidate(); + } public void setY(String y) { mY = SVGLength.from(y); invalidate(); } - @ReactProp(name = "width") - public void setWidth(Dynamic width) { - mW = SVGLength.from(width); - invalidate(); - } + @ReactProp(name = "width") + public void setWidth(Dynamic width) { + mW = SVGLength.from(width); + invalidate(); + } public void setWidth(String width) { mW = SVGLength.from(width); invalidate(); } - @ReactProp(name = "height") - public void setHeight(Dynamic height) { - mH = SVGLength.from(height); - invalidate(); - } + @ReactProp(name = "height") + public void setHeight(Dynamic height) { + mH = SVGLength.from(height); + invalidate(); + } public void setHeight(String height) { mH = SVGLength.from(height); invalidate(); } - @ReactProp(name = "rx") - public void setRx(Dynamic rx) { - mRx = SVGLength.from(rx); - invalidate(); - } + @ReactProp(name = "rx") + public void setRx(Dynamic rx) { + mRx = SVGLength.from(rx); + invalidate(); + } public void setRx(String rx) { mRx = SVGLength.from(rx); invalidate(); } - @ReactProp(name = "ry") - public void setRy(Dynamic ry) { - mRy = SVGLength.from(ry); - invalidate(); - } + @ReactProp(name = "ry") + public void setRy(Dynamic ry) { + mRy = SVGLength.from(ry); + invalidate(); + } public void setRy(String ry) { mRy = SVGLength.from(ry); invalidate(); } - @Override - Path getPath(Canvas canvas, Paint paint) { - Path path = new Path(); - double x = relativeOnWidth(mX); - double y = relativeOnHeight(mY); - double w = relativeOnWidth(mW); - double h = relativeOnHeight(mH); + @Override + Path getPath(Canvas canvas, Paint paint) { + Path path = new Path(); + double x = relativeOnWidth(mX); + double y = relativeOnHeight(mY); + double w = relativeOnWidth(mW); + double h = relativeOnHeight(mH); - if (mRx != null || mRy != null) { - double rx = 0d; - double ry = 0d; - if (mRx == null) { - ry = relativeOnHeight(mRy); - rx = ry; - } else if (mRy == null) { - rx = relativeOnWidth(mRx); - ry = rx; - } else { - rx = relativeOnWidth(mRx); - ry = relativeOnHeight(mRy); - } + if (mRx != null || mRy != null) { + double rx = 0d; + double ry = 0d; + if (mRx == null) { + ry = relativeOnHeight(mRy); + rx = ry; + } else if (mRy == null) { + rx = relativeOnWidth(mRx); + ry = rx; + } else { + rx = relativeOnWidth(mRx); + ry = relativeOnHeight(mRy); + } - if (rx > w / 2) { - rx = w / 2; - } + if (rx > w / 2) { + rx = w / 2; + } - if (ry > h / 2) { - ry = h / 2; - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - path.addRoundRect((float) x, (float) y, (float) (x + w), (float) (y + h), (float) rx, (float) ry, Path.Direction.CW); - } else { - path.addRoundRect(new RectF((float) x, (float) y, (float) (x + w), (float) (y + h)), (float) rx, (float) ry, Path.Direction.CW); - } - } else { - path.addRect((float) x, (float) y, (float) (x + w), (float) (y + h), Path.Direction.CW); - path.close(); // Ensure isSimplePath = false such that rect doesn't become represented using integers - } - return path; + if (ry > h / 2) { + ry = h / 2; + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + path.addRoundRect( + (float) x, + (float) y, + (float) (x + w), + (float) (y + h), + (float) rx, + (float) ry, + Path.Direction.CW); + } else { + path.addRoundRect( + new RectF((float) x, (float) y, (float) (x + w), (float) (y + h)), + (float) rx, + (float) ry, + Path.Direction.CW); + } + } else { + path.addRect((float) x, (float) y, (float) (x + w), (float) (y + h), Path.Direction.CW); + path.close(); // Ensure isSimplePath = false such that rect doesn't become represented using + // integers } + return path; + } } diff --git a/android/src/main/java/com/horcrux/svg/RenderableView.java b/android/src/main/java/com/horcrux/svg/RenderableView.java index edfb788b..11d8e381 100644 --- a/android/src/main/java/com/horcrux/svg/RenderableView.java +++ b/android/src/main/java/com/horcrux/svg/RenderableView.java @@ -6,7 +6,6 @@ * LICENSE file in the root directory of this source tree. */ - package com.horcrux.svg; import android.graphics.Bitmap; @@ -20,7 +19,7 @@ import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; - +import com.facebook.react.bridge.ColorPropConverter; import com.facebook.react.bridge.Dynamic; import com.facebook.react.bridge.JSApplicationIllegalArgumentException; import com.facebook.react.bridge.JavaOnlyArray; @@ -28,122 +27,118 @@ import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.ReadableType; -import com.facebook.react.bridge.ColorPropConverter; import com.facebook.react.uimanager.PointerEvents; import com.facebook.react.uimanager.annotations.ReactProp; - import java.lang.reflect.Field; import java.util.ArrayList; import java.util.regex.Matcher; import java.util.regex.Pattern; - import javax.annotation.Nullable; @SuppressWarnings({"WeakerAccess", "RedundantSuppression"}) -abstract public class RenderableView extends VirtualView { +public abstract class RenderableView extends VirtualView { - RenderableView(ReactContext reactContext) { - super(reactContext); - setPivotX(0); - setPivotY(0); + RenderableView(ReactContext reactContext) { + super(reactContext); + setPivotX(0); + setPivotY(0); + } + + static RenderableView contextElement; + // strokeLinecap + private static final int CAP_BUTT = 0; + static final int CAP_ROUND = 1; + private static final int CAP_SQUARE = 2; + + // strokeLinejoin + private static final int JOIN_BEVEL = 2; + private static final int JOIN_MITER = 0; + static final int JOIN_ROUND = 1; + + // fillRule + private static final int FILL_RULE_EVENODD = 0; + static final int FILL_RULE_NONZERO = 1; + + // vectorEffect + private static final int VECTOR_EFFECT_DEFAULT = 0; + private static final int VECTOR_EFFECT_NON_SCALING_STROKE = 1; + // static final int VECTOR_EFFECT_INHERIT = 2; + // static final int VECTOR_EFFECT_URI = 3; + + /* + Used in mergeProperties, keep public + */ + + public int vectorEffect = VECTOR_EFFECT_DEFAULT; + public @Nullable ReadableArray stroke; + public @Nullable SVGLength[] strokeDasharray; + + public SVGLength strokeWidth = new SVGLength(1); + public float strokeOpacity = 1; + public float strokeMiterlimit = 4; + public float strokeDashoffset = 0; + + public Paint.Cap strokeLinecap = Paint.Cap.BUTT; + public Paint.Join strokeLinejoin = Paint.Join.MITER; + + public @Nullable ReadableArray fill; + public float fillOpacity = 1; + public Path.FillType fillRule = Path.FillType.WINDING; + + /* + End merged properties + */ + private @Nullable ArrayList mLastMergedList; + private @Nullable ArrayList mOriginProperties; + private @Nullable ArrayList mPropList; + private @Nullable ArrayList mAttributeList; + + private static final Pattern regex = Pattern.compile("[0-9.-]+"); + + @Override + public void setId(int id) { + super.setId(id); + RenderableViewManager.setRenderableView(id, this); + } + + @ReactProp(name = "vectorEffect") + public void setVectorEffect(int vectorEffect) { + this.vectorEffect = vectorEffect; + invalidate(); + } + + @ReactProp(name = "fill") + public void setFill(@Nullable Dynamic fill) { + if (fill == null || fill.isNull()) { + this.fill = null; + invalidate(); + return; } - static RenderableView contextElement; - // strokeLinecap - private static final int CAP_BUTT = 0; - static final int CAP_ROUND = 1; - private static final int CAP_SQUARE = 2; - - // strokeLinejoin - private static final int JOIN_BEVEL = 2; - private static final int JOIN_MITER = 0; - static final int JOIN_ROUND = 1; - - // fillRule - private static final int FILL_RULE_EVENODD = 0; - static final int FILL_RULE_NONZERO = 1; - - // vectorEffect - private static final int VECTOR_EFFECT_DEFAULT = 0; - private static final int VECTOR_EFFECT_NON_SCALING_STROKE = 1; - //static final int VECTOR_EFFECT_INHERIT = 2; - //static final int VECTOR_EFFECT_URI = 3; - - /* - Used in mergeProperties, keep public - */ - - public int vectorEffect = VECTOR_EFFECT_DEFAULT; - public @Nullable ReadableArray stroke; - public @Nullable SVGLength[] strokeDasharray; - - public SVGLength strokeWidth = new SVGLength(1); - public float strokeOpacity = 1; - public float strokeMiterlimit = 4; - public float strokeDashoffset = 0; - - public Paint.Cap strokeLinecap = Paint.Cap.BUTT; - public Paint.Join strokeLinejoin = Paint.Join.MITER; - - public @Nullable ReadableArray fill; - public float fillOpacity = 1; - public Path.FillType fillRule = Path.FillType.WINDING; - - /* - End merged properties - */ - private @Nullable ArrayList mLastMergedList; - private @Nullable ArrayList mOriginProperties; - private @Nullable ArrayList mPropList; - private @Nullable ArrayList mAttributeList; - - private static final Pattern regex = Pattern.compile("[0-9.-]+"); - - @Override - public void setId(int id) { - super.setId(id); - RenderableViewManager.setRenderableView(id, this); + ReadableType fillType = fill.getType(); + if (fillType.equals(ReadableType.Map)) { + ReadableMap fillMap = fill.asMap(); + setFill(fillMap); } - @ReactProp(name = "vectorEffect") - public void setVectorEffect(int vectorEffect) { - this.vectorEffect = vectorEffect; - invalidate(); - } - - @ReactProp(name = "fill") - public void setFill(@Nullable Dynamic fill) { - if (fill == null || fill.isNull()) { - this.fill = null; - invalidate(); - return; - } - - ReadableType fillType = fill.getType(); - if (fillType.equals(ReadableType.Map)) { - ReadableMap fillMap = fill.asMap(); - setFill(fillMap); - } - - - // This code will probably never be reached with current changes - if (fillType.equals(ReadableType.Number)) { - this.fill = JavaOnlyArray.of(0, fill.asInt()); - } else if (fillType.equals(ReadableType.Array)) { - this.fill = fill.asArray(); - } else { - JavaOnlyArray arr = new JavaOnlyArray(); - arr.pushInt(0); - Matcher m = regex.matcher(fill.asString()); - int i = 0; - while (m.find()) { - double parsed = Double.parseDouble(m.group()); - arr.pushDouble(i++ < 3 ? parsed / 255 : parsed); - } - this.fill = arr; - } - invalidate(); + // This code will probably never be reached with current changes + if (fillType.equals(ReadableType.Number)) { + this.fill = JavaOnlyArray.of(0, fill.asInt()); + } else if (fillType.equals(ReadableType.Array)) { + this.fill = fill.asArray(); + } else { + JavaOnlyArray arr = new JavaOnlyArray(); + arr.pushInt(0); + Matcher m = regex.matcher(fill.asString()); + int i = 0; + while (m.find()) { + double parsed = Double.parseDouble(m.group()); + arr.pushDouble(i++ < 3 ? parsed / 255 : parsed); + } + this.fill = arr; } + invalidate(); + } public void setFill(ReadableMap fill) { if (fill == null) { @@ -167,53 +162,52 @@ abstract public class RenderableView extends VirtualView { invalidate(); } - @ReactProp(name = "fillOpacity", defaultFloat = 1f) - public void setFillOpacity(float fillOpacity) { - this.fillOpacity = fillOpacity; - invalidate(); + @ReactProp(name = "fillOpacity", defaultFloat = 1f) + public void setFillOpacity(float fillOpacity) { + this.fillOpacity = fillOpacity; + invalidate(); + } + + @ReactProp(name = "fillRule", defaultInt = FILL_RULE_NONZERO) + public void setFillRule(int fillRule) { + switch (fillRule) { + case FILL_RULE_EVENODD: + this.fillRule = Path.FillType.EVEN_ODD; + break; + case FILL_RULE_NONZERO: + break; + default: + throw new JSApplicationIllegalArgumentException("fillRule " + fillRule + " unrecognized"); } - @ReactProp(name = "fillRule", defaultInt = FILL_RULE_NONZERO) - public void setFillRule(int fillRule) { - switch (fillRule) { - case FILL_RULE_EVENODD: - this.fillRule = Path.FillType.EVEN_ODD; - break; - case FILL_RULE_NONZERO: - break; - default: - throw new JSApplicationIllegalArgumentException( - "fillRule " + fillRule + " unrecognized"); - } + invalidate(); + } - invalidate(); + @ReactProp(name = "stroke") + public void setStroke(@Nullable Dynamic strokeColors) { + if (strokeColors == null || strokeColors.isNull()) { + stroke = null; + invalidate(); + return; } - - @ReactProp(name = "stroke") - public void setStroke(@Nullable Dynamic strokeColors) { - if (strokeColors == null || strokeColors.isNull()) { - stroke = null; - invalidate(); - return; - } - ReadableType type = strokeColors.getType(); - if (type.equals(ReadableType.Number)) { - stroke = JavaOnlyArray.of(0, strokeColors.asInt()); - } else if (type.equals(ReadableType.Array)) { - stroke = strokeColors.asArray(); - } else { - JavaOnlyArray arr = new JavaOnlyArray(); - arr.pushInt(0); - Matcher m = regex.matcher(strokeColors.asString()); - int i = 0; - while (m.find()) { - double parsed = Double.parseDouble(m.group()); - arr.pushDouble(i++ < 3 ? parsed / 255 : parsed); - } - stroke = arr; - } - invalidate(); + ReadableType type = strokeColors.getType(); + if (type.equals(ReadableType.Number)) { + stroke = JavaOnlyArray.of(0, strokeColors.asInt()); + } else if (type.equals(ReadableType.Array)) { + stroke = strokeColors.asArray(); + } else { + JavaOnlyArray arr = new JavaOnlyArray(); + arr.pushInt(0); + Matcher m = regex.matcher(strokeColors.asString()); + int i = 0; + while (m.find()) { + double parsed = Double.parseDouble(m.group()); + arr.pushDouble(i++ < 3 ? parsed / 255 : parsed); + } + stroke = arr; } + invalidate(); + } public void setStroke(@Nullable ReadableMap stroke) { if (stroke == null) { @@ -237,484 +231,485 @@ abstract public class RenderableView extends VirtualView { invalidate(); } - @ReactProp(name = "strokeOpacity", defaultFloat = 1f) - public void setStrokeOpacity(float strokeOpacity) { - this.strokeOpacity = strokeOpacity; - invalidate(); - } + @ReactProp(name = "strokeOpacity", defaultFloat = 1f) + public void setStrokeOpacity(float strokeOpacity) { + this.strokeOpacity = strokeOpacity; + invalidate(); + } - @ReactProp(name = "strokeDasharray") - public void setStrokeDasharray(@Nullable ReadableArray strokeDasharray) { - if (strokeDasharray != null) { - int fromSize = strokeDasharray.size(); - this.strokeDasharray = new SVGLength[fromSize]; - for (int i = 0; i < fromSize; i++) { - this.strokeDasharray[i] = SVGLength.from(strokeDasharray.getDynamic(i)); - } - } else { - this.strokeDasharray = null; - } - invalidate(); + @ReactProp(name = "strokeDasharray") + public void setStrokeDasharray(@Nullable ReadableArray strokeDasharray) { + if (strokeDasharray != null) { + int fromSize = strokeDasharray.size(); + this.strokeDasharray = new SVGLength[fromSize]; + for (int i = 0; i < fromSize; i++) { + this.strokeDasharray[i] = SVGLength.from(strokeDasharray.getDynamic(i)); + } + } else { + this.strokeDasharray = null; } + invalidate(); + } - @ReactProp(name = "strokeDashoffset") - public void setStrokeDashoffset(float strokeDashoffset) { - this.strokeDashoffset = strokeDashoffset * mScale; - invalidate(); - } + @ReactProp(name = "strokeDashoffset") + public void setStrokeDashoffset(float strokeDashoffset) { + this.strokeDashoffset = strokeDashoffset * mScale; + invalidate(); + } - @ReactProp(name = "strokeWidth") - public void setStrokeWidth(Dynamic strokeWidth) { - this.strokeWidth = SVGLength.from(strokeWidth); - invalidate(); - } + @ReactProp(name = "strokeWidth") + public void setStrokeWidth(Dynamic strokeWidth) { + this.strokeWidth = SVGLength.from(strokeWidth); + invalidate(); + } public void setStrokeWidth(String strokeWidth) { this.strokeWidth = SVGLength.from(strokeWidth); invalidate(); } - @ReactProp(name = "strokeMiterlimit", defaultFloat = 4f) - public void setStrokeMiterlimit(float strokeMiterlimit) { - this.strokeMiterlimit = strokeMiterlimit; - invalidate(); + @ReactProp(name = "strokeMiterlimit", defaultFloat = 4f) + public void setStrokeMiterlimit(float strokeMiterlimit) { + this.strokeMiterlimit = strokeMiterlimit; + invalidate(); + } + + @ReactProp(name = "strokeLinecap", defaultInt = CAP_ROUND) + public void setStrokeLinecap(int strokeLinecap) { + switch (strokeLinecap) { + case CAP_BUTT: + this.strokeLinecap = Paint.Cap.BUTT; + break; + case CAP_SQUARE: + this.strokeLinecap = Paint.Cap.SQUARE; + break; + case CAP_ROUND: + this.strokeLinecap = Paint.Cap.ROUND; + break; + default: + throw new JSApplicationIllegalArgumentException( + "strokeLinecap " + strokeLinecap + " unrecognized"); + } + invalidate(); + } + + @ReactProp(name = "strokeLinejoin", defaultInt = JOIN_ROUND) + public void setStrokeLinejoin(int strokeLinejoin) { + switch (strokeLinejoin) { + case JOIN_MITER: + this.strokeLinejoin = Paint.Join.MITER; + break; + case JOIN_BEVEL: + this.strokeLinejoin = Paint.Join.BEVEL; + break; + case JOIN_ROUND: + this.strokeLinejoin = Paint.Join.ROUND; + break; + default: + throw new JSApplicationIllegalArgumentException( + "strokeLinejoin " + strokeLinejoin + " unrecognized"); + } + invalidate(); + } + + @ReactProp(name = "propList") + public void setPropList(@Nullable ReadableArray propList) { + if (propList != null) { + mPropList = mAttributeList = new ArrayList<>(); + for (int i = 0; i < propList.size(); i++) { + mPropList.add(propList.getString(i)); + } } - @ReactProp(name = "strokeLinecap", defaultInt = CAP_ROUND) - public void setStrokeLinecap(int strokeLinecap) { - switch (strokeLinecap) { - case CAP_BUTT: - this.strokeLinecap = Paint.Cap.BUTT; - break; - case CAP_SQUARE: - this.strokeLinecap = Paint.Cap.SQUARE; - break; - case CAP_ROUND: - this.strokeLinecap = Paint.Cap.ROUND; - break; - default: - throw new JSApplicationIllegalArgumentException( - "strokeLinecap " + strokeLinecap + " unrecognized"); + invalidate(); + } + + private static double saturate(double v) { + return v <= 0 ? 0 : (v >= 1 ? 1 : v); + } + + void render(Canvas canvas, Paint paint, float opacity) { + MaskView mask = null; + if (mMask != null) { + SvgView root = getSvgView(); + mask = (MaskView) root.getDefinedMask(mMask); + } + if (mask != null) { + Rect clipBounds = canvas.getClipBounds(); + int height = clipBounds.height(); + int width = clipBounds.width(); + + Bitmap maskBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + Bitmap original = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + + Canvas originalCanvas = new Canvas(original); + Canvas maskCanvas = new Canvas(maskBitmap); + Canvas resultCanvas = new Canvas(result); + + // Clip to mask bounds and render the mask + float maskX = (float) relativeOnWidth(mask.mX); + float maskY = (float) relativeOnHeight(mask.mY); + float maskWidth = (float) relativeOnWidth(mask.mW); + float maskHeight = (float) relativeOnHeight(mask.mH); + maskCanvas.clipRect(maskX, maskY, maskWidth, maskHeight); + + Paint maskPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mask.draw(maskCanvas, maskPaint, 1); + + // Apply luminanceToAlpha filter primitive + // https://www.w3.org/TR/SVG11/filters.html#feColorMatrixElement + int nPixels = width * height; + int[] pixels = new int[nPixels]; + maskBitmap.getPixels(pixels, 0, width, 0, 0, width, height); + + for (int i = 0; i < nPixels; i++) { + int color = pixels[i]; + + int r = (color >> 16) & 0xFF; + int g = (color >> 8) & 0xFF; + int b = color & 0xFF; + int a = color >>> 24; + + double luminance = saturate(((0.299 * r) + (0.587 * g) + (0.144 * b)) / 255); + int alpha = (int) (a * luminance); + int pixel = (alpha << 24); + pixels[i] = pixel; + } + + maskBitmap.setPixels(pixels, 0, width, 0, 0, width, height); + + // Render content of current SVG Renderable to image + draw(originalCanvas, paint, opacity); + + // Blend current element and mask + maskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); + resultCanvas.drawBitmap(original, 0, 0, null); + resultCanvas.drawBitmap(maskBitmap, 0, 0, maskPaint); + + // Render composited result into current render context + canvas.drawBitmap(result, 0, 0, paint); + } else { + draw(canvas, paint, opacity); + } + } + + @Override + void draw(Canvas canvas, Paint paint, float opacity) { + opacity *= mOpacity; + + boolean computePaths = mPath == null; + if (computePaths) { + mPath = getPath(canvas, paint); + mPath.setFillType(fillRule); + } + boolean nonScalingStroke = vectorEffect == VECTOR_EFFECT_NON_SCALING_STROKE; + Path path = mPath; + if (nonScalingStroke) { + Path scaled = new Path(); + //noinspection deprecation + mPath.transform(mCTM, scaled); + canvas.setMatrix(null); + path = scaled; + } + + if (computePaths || path != mPath) { + mBox = new RectF(); + path.computeBounds(mBox, true); + } + + RectF clientRect = new RectF(mBox); + mCTM.mapRect(clientRect); + this.setClientRect(clientRect); + + clip(canvas, paint); + + if (setupFillPaint(paint, opacity * fillOpacity)) { + if (computePaths) { + mFillPath = new Path(); + paint.getFillPath(path, mFillPath); + } + canvas.drawPath(path, paint); + } + if (setupStrokePaint(paint, opacity * strokeOpacity)) { + if (computePaths) { + mStrokePath = new Path(); + paint.getFillPath(path, mStrokePath); + } + canvas.drawPath(path, paint); + } + renderMarkers(canvas, paint, opacity); + } + + void renderMarkers(Canvas canvas, Paint paint, float opacity) { + MarkerView markerStart = (MarkerView) getSvgView().getDefinedMarker(mMarkerStart); + MarkerView markerMid = (MarkerView) getSvgView().getDefinedMarker(mMarkerMid); + MarkerView markerEnd = (MarkerView) getSvgView().getDefinedMarker(mMarkerEnd); + if (elements != null && (markerStart != null || markerMid != null || markerEnd != null)) { + contextElement = this; + ArrayList positions = RNSVGMarkerPosition.fromPath(elements); + float width = (float) (this.strokeWidth != null ? relativeOnOther(this.strokeWidth) : 1); + mMarkerPath = new Path(); + for (RNSVGMarkerPosition position : positions) { + RNSVGMarkerType type = position.type; + MarkerView marker = null; + switch (type) { + case kStartMarker: + marker = markerStart; + break; + + case kMidMarker: + marker = markerMid; + break; + + case kEndMarker: + marker = markerEnd; + break; } - invalidate(); - } - - @ReactProp(name = "strokeLinejoin", defaultInt = JOIN_ROUND) - public void setStrokeLinejoin(int strokeLinejoin) { - switch (strokeLinejoin) { - case JOIN_MITER: - this.strokeLinejoin = Paint.Join.MITER; - break; - case JOIN_BEVEL: - this.strokeLinejoin = Paint.Join.BEVEL; - break; - case JOIN_ROUND: - this.strokeLinejoin = Paint.Join.ROUND; - break; - default: - throw new JSApplicationIllegalArgumentException( - "strokeLinejoin " + strokeLinejoin + " unrecognized"); + if (marker == null) { + continue; } - invalidate(); + marker.renderMarker(canvas, paint, opacity, position, width); + Matrix transform = marker.markerTransform; + mMarkerPath.addPath(marker.getPath(canvas, paint), transform); + } + contextElement = null; + } + } + + /** + * Sets up paint according to the props set on a view. Returns {@code true} if the fill should be + * drawn, {@code false} if not. + */ + boolean setupFillPaint(Paint paint, float opacity) { + if (fill != null && fill.size() > 0) { + paint.reset(); + paint.setFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG | Paint.SUBPIXEL_TEXT_FLAG); + paint.setStyle(Paint.Style.FILL); + setupPaint(paint, opacity, fill); + return true; + } + return false; + } + + /** + * Sets up paint according to the props set on a view. Returns {@code true} if the stroke should + * be drawn, {@code false} if not. + */ + boolean setupStrokePaint(Paint paint, float opacity) { + paint.reset(); + double strokeWidth = relativeOnOther(this.strokeWidth); + if (strokeWidth == 0 || stroke == null || stroke.size() == 0) { + return false; } - @ReactProp(name = "propList") - public void setPropList(@Nullable ReadableArray propList) { - if (propList != null) { - mPropList = mAttributeList = new ArrayList<>(); - for (int i = 0; i < propList.size(); i++) { - mPropList.add(propList.getString(i)); - } - } + paint.setFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG | Paint.SUBPIXEL_TEXT_FLAG); + paint.setStyle(Paint.Style.STROKE); + paint.setStrokeCap(strokeLinecap); + paint.setStrokeJoin(strokeLinejoin); + paint.setStrokeMiter(strokeMiterlimit * mScale); + paint.setStrokeWidth((float) strokeWidth); + setupPaint(paint, opacity, stroke); - invalidate(); + if (strokeDasharray != null) { + int length = strokeDasharray.length; + float[] intervals = new float[length]; + for (int i = 0; i < length; i++) { + intervals[i] = (float) relativeOnOther(strokeDasharray[i]); + } + paint.setPathEffect(new DashPathEffect(intervals, strokeDashoffset)); } - private static double saturate(double v) { - return v <= 0 ? 0 : (v >= 1 ? 1 : v); - } + return true; + } - void render(Canvas canvas, Paint paint, float opacity) { - MaskView mask = null; - if (mMask != null) { - SvgView root = getSvgView(); - mask = (MaskView) root.getDefinedMask(mMask); - } - if (mask != null) { - Rect clipBounds = canvas.getClipBounds(); - int height = clipBounds.height(); - int width = clipBounds.width(); - - Bitmap maskBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - Bitmap original = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - - Canvas originalCanvas = new Canvas(original); - Canvas maskCanvas = new Canvas(maskBitmap); - Canvas resultCanvas = new Canvas(result); - - // Clip to mask bounds and render the mask - float maskX = (float) relativeOnWidth(mask.mX); - float maskY = (float) relativeOnHeight(mask.mY); - float maskWidth = (float) relativeOnWidth(mask.mW); - float maskHeight = (float) relativeOnHeight(mask.mH); - maskCanvas.clipRect(maskX, maskY, maskWidth, maskHeight); - - Paint maskPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mask.draw(maskCanvas, maskPaint, 1); - - // Apply luminanceToAlpha filter primitive https://www.w3.org/TR/SVG11/filters.html#feColorMatrixElement - int nPixels = width * height; - int[] pixels = new int[nPixels]; - maskBitmap.getPixels(pixels, 0, width, 0, 0, width, height); - - for (int i = 0; i < nPixels; i++) { - int color = pixels[i]; - - int r = (color >> 16) & 0xFF; - int g = (color >> 8) & 0xFF; - int b = color & 0xFF; - int a = color >>> 24; - - double luminance = saturate(((0.299 * r) + (0.587 * g) + (0.144 * b)) / 255); - int alpha = (int) (a * luminance); - int pixel = (alpha << 24); - pixels[i] = pixel; - } - - maskBitmap.setPixels(pixels, 0, width, 0, 0, width, height); - - // Render content of current SVG Renderable to image - draw(originalCanvas, paint, opacity); - - // Blend current element and mask - maskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); - resultCanvas.drawBitmap(original, 0, 0, null); - resultCanvas.drawBitmap(maskBitmap, 0, 0, maskPaint); - - // Render composited result into current render context - canvas.drawBitmap(result, 0, 0, paint); + private void setupPaint(Paint paint, float opacity, ReadableArray colors) { + int colorType = colors.getInt(0); + switch (colorType) { + case 0: + if (colors.size() == 2) { + int color; + if (colors.getType(1) == ReadableType.Map) { + color = ColorPropConverter.getColor(colors.getMap(1), getContext()); + } else { + color = colors.getInt(1); + } + int alpha = color >>> 24; + int combined = Math.round((float) alpha * opacity); + paint.setColor(combined << 24 | (color & 0x00ffffff)); } else { - draw(canvas, paint, opacity); + // solid color + paint.setARGB( + (int) (colors.size() > 4 ? colors.getDouble(4) * opacity * 255 : opacity * 255), + (int) (colors.getDouble(1) * 255), + (int) (colors.getDouble(2) * 255), + (int) (colors.getDouble(3) * 255)); + } + break; + case 1: + { + Brush brush = getSvgView().getDefinedBrush(colors.getString(1)); + if (brush != null) { + brush.setupPaint(paint, mBox, mScale, opacity); + } + break; + } + case 2: + { + int brush = getSvgView().mTintColor; + paint.setColor(brush); + break; + } + case 3: + { + if (contextElement != null && contextElement.fill != null) { + setupPaint(paint, opacity, contextElement.fill); + } + break; + } + case 4: + { + if (contextElement != null && contextElement.stroke != null) { + setupPaint(paint, opacity, contextElement.stroke); + } + break; } } + } - @Override - void draw(Canvas canvas, Paint paint, float opacity) { - opacity *= mOpacity; + abstract Path getPath(Canvas canvas, Paint paint); - boolean computePaths = mPath == null; - if (computePaths) { - mPath = getPath(canvas, paint); - mPath.setFillType(fillRule); - } - boolean nonScalingStroke = vectorEffect == VECTOR_EFFECT_NON_SCALING_STROKE; - Path path = mPath; - if (nonScalingStroke) { - Path scaled = new Path(); - //noinspection deprecation - mPath.transform(mCTM, scaled); - canvas.setMatrix(null); - path = scaled; - } - - if (computePaths || path != mPath) { - mBox = new RectF(); - path.computeBounds(mBox, true); - } - - RectF clientRect = new RectF(mBox); - mCTM.mapRect(clientRect); - this.setClientRect(clientRect); - - clip(canvas, paint); - - if (setupFillPaint(paint, opacity * fillOpacity)) { - if (computePaths) { - mFillPath = new Path(); - paint.getFillPath(path, mFillPath); - } - canvas.drawPath(path, paint); - } - if (setupStrokePaint(paint, opacity * strokeOpacity)) { - if (computePaths) { - mStrokePath = new Path(); - paint.getFillPath(path, mStrokePath); - } - canvas.drawPath(path, paint); - } - renderMarkers(canvas, paint, opacity); + @Override + int hitTest(final float[] src) { + if (mPath == null || !mInvertible || !mTransformInvertible) { + return -1; } - void renderMarkers(Canvas canvas, Paint paint, float opacity) { - MarkerView markerStart = (MarkerView)getSvgView().getDefinedMarker(mMarkerStart); - MarkerView markerMid = (MarkerView)getSvgView().getDefinedMarker(mMarkerMid); - MarkerView markerEnd = (MarkerView)getSvgView().getDefinedMarker(mMarkerEnd); - if (elements != null && (markerStart != null || markerMid != null || markerEnd != null)) { - contextElement = this; - ArrayList positions = RNSVGMarkerPosition.fromPath(elements); - float width = (float)(this.strokeWidth != null ? relativeOnOther(this.strokeWidth) : 1); - mMarkerPath = new Path(); - for (RNSVGMarkerPosition position : positions) { - RNSVGMarkerType type = position.type; - MarkerView marker = null; - switch (type) { - case kStartMarker: - marker = markerStart; - break; - - case kMidMarker: - marker = markerMid; - break; - - case kEndMarker: - marker = markerEnd; - break; - } - if (marker == null) { - continue; - } - marker.renderMarker(canvas, paint, opacity, position, width); - Matrix transform = marker.markerTransform; - mMarkerPath.addPath(marker.getPath(canvas, paint), transform); - } - contextElement = null; - } + if (mPointerEvents == PointerEvents.NONE) { + return -1; } - /** - * Sets up paint according to the props set on a view. Returns {@code true} - * if the fill should be drawn, {@code false} if not. - */ - boolean setupFillPaint(Paint paint, float opacity) { - if (fill != null && fill.size() > 0) { - paint.reset(); - paint.setFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG | Paint.SUBPIXEL_TEXT_FLAG); - paint.setStyle(Paint.Style.FILL); - setupPaint(paint, opacity, fill); - return true; - } - return false; + float[] dst = new float[2]; + mInvMatrix.mapPoints(dst, src); + mInvTransform.mapPoints(dst); + int x = Math.round(dst[0]); + int y = Math.round(dst[1]); + + initBounds(); + + if ((mRegion == null || !mRegion.contains(x, y)) + && (mStrokeRegion == null + || !mStrokeRegion.contains(x, y) + && (mMarkerRegion == null || !mMarkerRegion.contains(x, y)))) { + return -1; } - /** - * Sets up paint according to the props set on a view. Returns {@code true} - * if the stroke should be drawn, {@code false} if not. - */ - boolean setupStrokePaint(Paint paint, float opacity) { - paint.reset(); - double strokeWidth = relativeOnOther(this.strokeWidth); - if (strokeWidth == 0 || stroke == null || stroke.size() == 0) { - return false; - } - - paint.setFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG | Paint.SUBPIXEL_TEXT_FLAG); - paint.setStyle(Paint.Style.STROKE); - paint.setStrokeCap(strokeLinecap); - paint.setStrokeJoin(strokeLinejoin); - paint.setStrokeMiter(strokeMiterlimit * mScale); - paint.setStrokeWidth((float) strokeWidth); - setupPaint(paint, opacity, stroke); - - if (strokeDasharray != null) { - int length = strokeDasharray.length; - float[] intervals = new float[length]; - for (int i = 0; i < length; i++) { - intervals[i] = (float)relativeOnOther(strokeDasharray[i]); - } - paint.setPathEffect(new DashPathEffect(intervals, strokeDashoffset)); - } - - return true; + Path clipPath = getClipPath(); + if (clipPath != null) { + if (!mClipRegion.contains(x, y)) { + return -1; + } } - private void setupPaint(Paint paint, float opacity, ReadableArray colors) { - int colorType = colors.getInt(0); - switch (colorType) { - case 0: - if (colors.size() == 2) { - int color; - if (colors.getType(1) == ReadableType.Map) { - color = ColorPropConverter.getColor(colors.getMap(1), getContext()); - } else { - color = colors.getInt(1); - } - int alpha = color >>> 24; - int combined = Math.round((float)alpha * opacity); - paint.setColor(combined << 24 | (color & 0x00ffffff)); - } else { - // solid color - paint.setARGB( - (int) (colors.size() > 4 ? colors.getDouble(4) * opacity * 255 : opacity * 255), - (int) (colors.getDouble(1) * 255), - (int) (colors.getDouble(2) * 255), - (int) (colors.getDouble(3) * 255)); - } - break; - case 1: { - Brush brush = getSvgView().getDefinedBrush(colors.getString(1)); - if (brush != null) { - brush.setupPaint(paint, mBox, mScale, opacity); - } - break; - } - case 2: { - int brush = getSvgView().mTintColor; - paint.setColor(brush); - break; - } - case 3: { - if (contextElement != null && contextElement.fill != null) { - setupPaint(paint, opacity, contextElement.fill); - } - break; - } - case 4: { - if (contextElement != null && contextElement.stroke != null) { - setupPaint(paint, opacity, contextElement.stroke); - } - break; - } - } + return getId(); + } + void initBounds() { + if (mRegion == null && mFillPath != null) { + mFillBounds = new RectF(); + mFillPath.computeBounds(mFillBounds, true); + mRegion = getRegion(mFillPath, mFillBounds); + } + if (mRegion == null && mPath != null) { + mFillBounds = new RectF(); + mPath.computeBounds(mFillBounds, true); + mRegion = getRegion(mPath, mFillBounds); + } + if (mStrokeRegion == null && mStrokePath != null) { + mStrokeBounds = new RectF(); + mStrokePath.computeBounds(mStrokeBounds, true); + mStrokeRegion = getRegion(mStrokePath, mStrokeBounds); + } + if (mMarkerRegion == null && mMarkerPath != null) { + mMarkerBounds = new RectF(); + mMarkerPath.computeBounds(mMarkerBounds, true); + mMarkerRegion = getRegion(mMarkerPath, mMarkerBounds); + } + Path clipPath = getClipPath(); + if (clipPath != null) { + if (mClipRegionPath != clipPath) { + mClipRegionPath = clipPath; + mClipBounds = new RectF(); + clipPath.computeBounds(mClipBounds, true); + mClipRegion = getRegion(clipPath, mClipBounds); + } + } + } + + Region getRegion(Path path, RectF rectF) { + Region region = new Region(); + region.setPath( + path, + new Region( + (int) Math.floor(rectF.left), + (int) Math.floor(rectF.top), + (int) Math.ceil(rectF.right), + (int) Math.ceil(rectF.bottom))); + + return region; + } + + private ArrayList getAttributeList() { + return mAttributeList; + } + + void mergeProperties(RenderableView target) { + ArrayList targetAttributeList = target.getAttributeList(); + + if (targetAttributeList == null || targetAttributeList.size() == 0) { + return; } - abstract Path getPath(Canvas canvas, Paint paint); + mOriginProperties = new ArrayList<>(); + mAttributeList = mPropList == null ? new ArrayList() : new ArrayList<>(mPropList); - @Override - int hitTest(final float[] src) { - if (mPath == null || !mInvertible || !mTransformInvertible) { - return -1; + for (int i = 0, size = targetAttributeList.size(); i < size; i++) { + try { + String fieldName = targetAttributeList.get(i); + Field field = getClass().getField(fieldName); + Object value = field.get(target); + mOriginProperties.add(field.get(this)); + + if (!hasOwnProperty(fieldName)) { + mAttributeList.add(fieldName); + field.set(this, value); } - - if (mPointerEvents == PointerEvents.NONE) { - return -1; - } - - float[] dst = new float[2]; - mInvMatrix.mapPoints(dst, src); - mInvTransform.mapPoints(dst); - int x = Math.round(dst[0]); - int y = Math.round(dst[1]); - - initBounds(); - - if ( - (mRegion == null || !mRegion.contains(x, y)) && - (mStrokeRegion == null || !mStrokeRegion.contains(x, y) && - (mMarkerRegion == null || !mMarkerRegion.contains(x, y))) - ) { - return -1; - } - - Path clipPath = getClipPath(); - if (clipPath != null) { - if (!mClipRegion.contains(x, y)) { - return -1; - } - } - - return getId(); + } catch (Exception e) { + throw new IllegalStateException(e); + } } - void initBounds() { - if (mRegion == null && mFillPath != null) { - mFillBounds = new RectF(); - mFillPath.computeBounds(mFillBounds, true); - mRegion = getRegion(mFillPath, mFillBounds); - } - if (mRegion == null && mPath != null) { - mFillBounds = new RectF(); - mPath.computeBounds(mFillBounds, true); - mRegion = getRegion(mPath, mFillBounds); - } - if (mStrokeRegion == null && mStrokePath != null) { - mStrokeBounds = new RectF(); - mStrokePath.computeBounds(mStrokeBounds, true); - mStrokeRegion = getRegion(mStrokePath, mStrokeBounds); - } - if (mMarkerRegion == null && mMarkerPath != null) { - mMarkerBounds = new RectF(); - mMarkerPath.computeBounds(mMarkerBounds, true); - mMarkerRegion = getRegion(mMarkerPath, mMarkerBounds); - } - Path clipPath = getClipPath(); - if (clipPath != null) { - if (mClipRegionPath != clipPath) { - mClipRegionPath = clipPath; - mClipBounds = new RectF(); - clipPath.computeBounds(mClipBounds, true); - mClipRegion = getRegion(clipPath, mClipBounds); - } + mLastMergedList = targetAttributeList; + } + + void resetProperties() { + if (mLastMergedList != null && mOriginProperties != null) { + try { + for (int i = mLastMergedList.size() - 1; i >= 0; i--) { + Field field = getClass().getField(mLastMergedList.get(i)); + field.set(this, mOriginProperties.get(i)); } + } catch (Exception e) { + throw new IllegalStateException(e); + } + + mLastMergedList = null; + mOriginProperties = null; + mAttributeList = mPropList; } + } - Region getRegion(Path path, RectF rectF) { - Region region = new Region(); - region.setPath(path, - new Region( - (int) Math.floor(rectF.left), - (int) Math.floor(rectF.top), - (int) Math.ceil(rectF.right), - (int) Math.ceil(rectF.bottom) - ) - ); - - return region; - } - - private ArrayList getAttributeList() { - return mAttributeList; - } - - void mergeProperties(RenderableView target) { - ArrayList targetAttributeList = target.getAttributeList(); - - if (targetAttributeList == null || - targetAttributeList.size() == 0) { - return; - } - - mOriginProperties = new ArrayList<>(); - mAttributeList = mPropList == null ? new ArrayList() : new ArrayList<>(mPropList); - - for (int i = 0, size = targetAttributeList.size(); i < size; i++) { - try { - String fieldName = targetAttributeList.get(i); - Field field = getClass().getField(fieldName); - Object value = field.get(target); - mOriginProperties.add(field.get(this)); - - if (!hasOwnProperty(fieldName)) { - mAttributeList.add(fieldName); - field.set(this, value); - } - } catch (Exception e) { - throw new IllegalStateException(e); - } - } - - mLastMergedList = targetAttributeList; - } - - void resetProperties() { - if (mLastMergedList != null && mOriginProperties != null) { - try { - for (int i = mLastMergedList.size() - 1; i >= 0; i--) { - Field field = getClass().getField(mLastMergedList.get(i)); - field.set(this, mOriginProperties.get(i)); - } - } catch (Exception e) { - throw new IllegalStateException(e); - } - - mLastMergedList = null; - mOriginProperties = null; - mAttributeList = mPropList; - } - } - - private boolean hasOwnProperty(String propName) { - return mAttributeList != null && mAttributeList.contains(propName); - } + private boolean hasOwnProperty(String propName) { + return mAttributeList != null && mAttributeList.contains(propName); + } } diff --git a/android/src/main/java/com/horcrux/svg/RenderableViewManager.java b/android/src/main/java/com/horcrux/svg/RenderableViewManager.java index 833c9a20..c5f6d764 100644 --- a/android/src/main/java/com/horcrux/svg/RenderableViewManager.java +++ b/android/src/main/java/com/horcrux/svg/RenderableViewManager.java @@ -6,77 +6,8 @@ * LICENSE file in the root directory of this source tree. */ - package com.horcrux.svg; -import android.graphics.Matrix; -import android.util.SparseArray; -import android.view.View; -import android.view.ViewGroup; - -import com.facebook.react.bridge.Dynamic; -import com.facebook.react.bridge.JavaOnlyMap; -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.bridge.ReadableType; -import com.facebook.react.uimanager.DisplayMetricsHolder; -import com.facebook.react.uimanager.LayoutShadowNode; -import com.facebook.react.uimanager.MatrixMathHelper; -import com.facebook.react.uimanager.PixelUtil; -import com.facebook.react.uimanager.PointerEvents; -import com.facebook.react.uimanager.ThemedReactContext; -import com.facebook.react.uimanager.TransformHelper; -import com.facebook.react.uimanager.ViewGroupManager; -import com.facebook.react.uimanager.ViewManagerDelegate; -import com.facebook.react.uimanager.ViewProps; -import com.facebook.react.uimanager.annotations.ReactProp; -import com.facebook.react.uimanager.annotations.ReactPropGroup; -import com.facebook.react.viewmanagers.RNSVGCircleManagerDelegate; -import com.facebook.react.viewmanagers.RNSVGCircleManagerInterface; -import com.facebook.react.viewmanagers.RNSVGClipPathManagerDelegate; -import com.facebook.react.viewmanagers.RNSVGClipPathManagerInterface; -import com.facebook.react.viewmanagers.RNSVGDefsManagerDelegate; -import com.facebook.react.viewmanagers.RNSVGDefsManagerInterface; -import com.facebook.react.viewmanagers.RNSVGEllipseManagerDelegate; -import com.facebook.react.viewmanagers.RNSVGEllipseManagerInterface; -import com.facebook.react.viewmanagers.RNSVGForeignObjectManagerDelegate; -import com.facebook.react.viewmanagers.RNSVGForeignObjectManagerInterface; -import com.facebook.react.viewmanagers.RNSVGGroupManagerDelegate; -import com.facebook.react.viewmanagers.RNSVGGroupManagerInterface; -import com.facebook.react.viewmanagers.RNSVGImageManagerDelegate; -import com.facebook.react.viewmanagers.RNSVGImageManagerInterface; -import com.facebook.react.viewmanagers.RNSVGLineManagerDelegate; -import com.facebook.react.viewmanagers.RNSVGLineManagerInterface; -import com.facebook.react.viewmanagers.RNSVGLinearGradientManagerDelegate; -import com.facebook.react.viewmanagers.RNSVGLinearGradientManagerInterface; -import com.facebook.react.viewmanagers.RNSVGMarkerManagerDelegate; -import com.facebook.react.viewmanagers.RNSVGMarkerManagerInterface; -import com.facebook.react.viewmanagers.RNSVGMaskManagerDelegate; -import com.facebook.react.viewmanagers.RNSVGMaskManagerInterface; -import com.facebook.react.viewmanagers.RNSVGPathManagerDelegate; -import com.facebook.react.viewmanagers.RNSVGPathManagerInterface; -import com.facebook.react.viewmanagers.RNSVGPatternManagerDelegate; -import com.facebook.react.viewmanagers.RNSVGPatternManagerInterface; -import com.facebook.react.viewmanagers.RNSVGRadialGradientManagerDelegate; -import com.facebook.react.viewmanagers.RNSVGRadialGradientManagerInterface; -import com.facebook.react.viewmanagers.RNSVGRectManagerDelegate; -import com.facebook.react.viewmanagers.RNSVGRectManagerInterface; -import com.facebook.react.viewmanagers.RNSVGSymbolManagerDelegate; -import com.facebook.react.viewmanagers.RNSVGSymbolManagerInterface; -import com.facebook.react.viewmanagers.RNSVGTSpanManagerDelegate; -import com.facebook.react.viewmanagers.RNSVGTSpanManagerInterface; -import com.facebook.react.viewmanagers.RNSVGTextManagerDelegate; -import com.facebook.react.viewmanagers.RNSVGTextManagerInterface; -import com.facebook.react.viewmanagers.RNSVGTextPathManagerDelegate; -import com.facebook.react.viewmanagers.RNSVGTextPathManagerInterface; -import com.facebook.react.viewmanagers.RNSVGUseManagerDelegate; -import com.facebook.react.viewmanagers.RNSVGUseManagerInterface; - -import java.util.Locale; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - import static com.facebook.react.uimanager.MatrixMathHelper.determinant; import static com.facebook.react.uimanager.MatrixMathHelper.inverse; import static com.facebook.react.uimanager.MatrixMathHelper.multiplyVectorByMatrix; @@ -142,9 +73,72 @@ import static com.horcrux.svg.RenderableView.CAP_ROUND; import static com.horcrux.svg.RenderableView.FILL_RULE_NONZERO; import static com.horcrux.svg.RenderableView.JOIN_ROUND; -/** - * ViewManager for DefinitionView RNSVG views - */ +import android.graphics.Matrix; +import android.util.SparseArray; +import android.view.View; +import android.view.ViewGroup; +import com.facebook.react.bridge.Dynamic; +import com.facebook.react.bridge.JavaOnlyMap; +import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.bridge.ReadableType; +import com.facebook.react.uimanager.DisplayMetricsHolder; +import com.facebook.react.uimanager.LayoutShadowNode; +import com.facebook.react.uimanager.MatrixMathHelper; +import com.facebook.react.uimanager.PixelUtil; +import com.facebook.react.uimanager.PointerEvents; +import com.facebook.react.uimanager.ThemedReactContext; +import com.facebook.react.uimanager.TransformHelper; +import com.facebook.react.uimanager.ViewGroupManager; +import com.facebook.react.uimanager.ViewManagerDelegate; +import com.facebook.react.uimanager.ViewProps; +import com.facebook.react.uimanager.annotations.ReactProp; +import com.facebook.react.uimanager.annotations.ReactPropGroup; +import com.facebook.react.viewmanagers.RNSVGCircleManagerDelegate; +import com.facebook.react.viewmanagers.RNSVGCircleManagerInterface; +import com.facebook.react.viewmanagers.RNSVGClipPathManagerDelegate; +import com.facebook.react.viewmanagers.RNSVGClipPathManagerInterface; +import com.facebook.react.viewmanagers.RNSVGDefsManagerDelegate; +import com.facebook.react.viewmanagers.RNSVGDefsManagerInterface; +import com.facebook.react.viewmanagers.RNSVGEllipseManagerDelegate; +import com.facebook.react.viewmanagers.RNSVGEllipseManagerInterface; +import com.facebook.react.viewmanagers.RNSVGForeignObjectManagerDelegate; +import com.facebook.react.viewmanagers.RNSVGForeignObjectManagerInterface; +import com.facebook.react.viewmanagers.RNSVGGroupManagerDelegate; +import com.facebook.react.viewmanagers.RNSVGGroupManagerInterface; +import com.facebook.react.viewmanagers.RNSVGImageManagerDelegate; +import com.facebook.react.viewmanagers.RNSVGImageManagerInterface; +import com.facebook.react.viewmanagers.RNSVGLineManagerDelegate; +import com.facebook.react.viewmanagers.RNSVGLineManagerInterface; +import com.facebook.react.viewmanagers.RNSVGLinearGradientManagerDelegate; +import com.facebook.react.viewmanagers.RNSVGLinearGradientManagerInterface; +import com.facebook.react.viewmanagers.RNSVGMarkerManagerDelegate; +import com.facebook.react.viewmanagers.RNSVGMarkerManagerInterface; +import com.facebook.react.viewmanagers.RNSVGMaskManagerDelegate; +import com.facebook.react.viewmanagers.RNSVGMaskManagerInterface; +import com.facebook.react.viewmanagers.RNSVGPathManagerDelegate; +import com.facebook.react.viewmanagers.RNSVGPathManagerInterface; +import com.facebook.react.viewmanagers.RNSVGPatternManagerDelegate; +import com.facebook.react.viewmanagers.RNSVGPatternManagerInterface; +import com.facebook.react.viewmanagers.RNSVGRadialGradientManagerDelegate; +import com.facebook.react.viewmanagers.RNSVGRadialGradientManagerInterface; +import com.facebook.react.viewmanagers.RNSVGRectManagerDelegate; +import com.facebook.react.viewmanagers.RNSVGRectManagerInterface; +import com.facebook.react.viewmanagers.RNSVGSymbolManagerDelegate; +import com.facebook.react.viewmanagers.RNSVGSymbolManagerInterface; +import com.facebook.react.viewmanagers.RNSVGTSpanManagerDelegate; +import com.facebook.react.viewmanagers.RNSVGTSpanManagerInterface; +import com.facebook.react.viewmanagers.RNSVGTextManagerDelegate; +import com.facebook.react.viewmanagers.RNSVGTextManagerInterface; +import com.facebook.react.viewmanagers.RNSVGTextPathManagerDelegate; +import com.facebook.react.viewmanagers.RNSVGTextPathManagerInterface; +import com.facebook.react.viewmanagers.RNSVGUseManagerDelegate; +import com.facebook.react.viewmanagers.RNSVGUseManagerInterface; +import java.util.Locale; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** ViewManager for DefinitionView RNSVG views */ class VirtualViewManager extends ViewGroupManager { protected final SVGClass svgClass; @@ -159,69 +153,67 @@ class VirtualViewManager extends ViewGroupManager extends ViewGroupManager extends ViewGroupManager extends ViewGroupManager extends ViewGroupManager extends ViewGroupManager PERSPECTIVE_ARRAY_INVERTED_CAMERA_DISTANCE_INDEX) { - float invertedCameraDistance = (float) perspectiveArray[PERSPECTIVE_ARRAY_INVERTED_CAMERA_DISTANCE_INDEX]; + float invertedCameraDistance = + (float) perspectiveArray[PERSPECTIVE_ARRAY_INVERTED_CAMERA_DISTANCE_INDEX]; if (invertedCameraDistance == 0) { // Default camera distance, before scale multiplier (1280) invertedCameraDistance = 0.00078125f; @@ -399,9 +391,9 @@ class VirtualViewManager extends ViewGroupManager extends ViewGroupManager extends ViewGroupManager extends ViewGroupManager extends ViewGroupManager mTagToRenderableView = new SparseArray<>(); private static final SparseArray mTagToRunnable = new SparseArray<>(); @@ -652,86 +644,85 @@ class VirtualViewManager extends ViewGroupManager extends VirtualViewManager { RenderableViewManager(SVGClass svgclass) { super(svgclass); } - static class GroupViewManagerAbstract extends RenderableViewManager { - GroupViewManagerAbstract(SVGClass svgClass) { - super(svgClass); - } - @ReactProp(name = "font") - public void setFont(U node, @Nullable ReadableMap font) { - node.setFont(font); - } - - @ReactProp(name = "fontSize") - public void setFontSize(U node, Dynamic fontSize) { - JavaOnlyMap map = new JavaOnlyMap(); - switch (fontSize.getType()) { - case Number: - map.putDouble("fontSize", fontSize.asDouble()); - break; - case String: - map.putString("fontSize", fontSize.asString()); - break; - default: - return; - } - node.setFont(map); - } - - public void setFontSize(U view, @Nullable String value) { - JavaOnlyMap map = new JavaOnlyMap(); - map.putString("fontSize", value); - view.setFont(map); - } - - @ReactProp(name = "fontWeight") - public void setFontWeight(U node, Dynamic fontWeight) { - JavaOnlyMap map = new JavaOnlyMap(); - switch (fontWeight.getType()) { - case Number: - map.putDouble("fontWeight", fontWeight.asDouble()); - break; - case String: - map.putString("fontWeight", fontWeight.asString()); - break; - default: - return; - } - node.setFont(map); - } - - public void setFontWeight(U view, @Nullable String value) { - JavaOnlyMap map = new JavaOnlyMap(); - map.putString("fontWeight", value); - view.setFont(map); - } + static class GroupViewManagerAbstract extends RenderableViewManager { + GroupViewManagerAbstract(SVGClass svgClass) { + super(svgClass); } - static class GroupViewManager extends GroupViewManagerAbstract implements RNSVGGroupManagerInterface { - GroupViewManager() { - super(SVGClass.RNSVGGroup); - mDelegate = new RNSVGGroupManagerDelegate(this); - } - - private final ViewManagerDelegate mDelegate; - - protected ViewManagerDelegate getDelegate(){ - return mDelegate; - } - + @ReactProp(name = "font") + public void setFont(U node, @Nullable ReadableMap font) { + node.setFont(font); } - static class PathViewManager extends RenderableViewManager implements RNSVGPathManagerInterface { + @ReactProp(name = "fontSize") + public void setFontSize(U node, Dynamic fontSize) { + JavaOnlyMap map = new JavaOnlyMap(); + switch (fontSize.getType()) { + case Number: + map.putDouble("fontSize", fontSize.asDouble()); + break; + case String: + map.putString("fontSize", fontSize.asString()); + break; + default: + return; + } + node.setFont(map); + } + + public void setFontSize(U view, @Nullable String value) { + JavaOnlyMap map = new JavaOnlyMap(); + map.putString("fontSize", value); + view.setFont(map); + } + + @ReactProp(name = "fontWeight") + public void setFontWeight(U node, Dynamic fontWeight) { + JavaOnlyMap map = new JavaOnlyMap(); + switch (fontWeight.getType()) { + case Number: + map.putDouble("fontWeight", fontWeight.asDouble()); + break; + case String: + map.putString("fontWeight", fontWeight.asString()); + break; + default: + return; + } + node.setFont(map); + } + + public void setFontWeight(U view, @Nullable String value) { + JavaOnlyMap map = new JavaOnlyMap(); + map.putString("fontWeight", value); + view.setFont(map); + } + } + + static class GroupViewManager extends GroupViewManagerAbstract + implements RNSVGGroupManagerInterface { + GroupViewManager() { + super(SVGClass.RNSVGGroup); + mDelegate = new RNSVGGroupManagerDelegate(this); + } + + private final ViewManagerDelegate mDelegate; + + protected ViewManagerDelegate getDelegate() { + return mDelegate; + } + } + + static class PathViewManager extends RenderableViewManager + implements RNSVGPathManagerInterface { PathViewManager() { super(SVGClass.RNSVGPath); mDelegate = new RNSVGPathManagerDelegate(this); @@ -739,15 +730,15 @@ class RenderableViewManager extends VirtualViewManager private final ViewManagerDelegate mDelegate; - protected ViewManagerDelegate getDelegate(){ + protected ViewManagerDelegate getDelegate() { return mDelegate; } - @ReactProp(name = "d") - public void setD(PathView node, String d) { - node.setD(d); - } + @ReactProp(name = "d") + public void setD(PathView node, String d) { + node.setD(d); } + } static class TextViewManagerAbstract extends GroupViewManagerAbstract { TextViewManagerAbstract(SVGClass svgClass) { @@ -863,7 +854,8 @@ class RenderableViewManager extends VirtualViewManager } } - static class TextViewManager extends TextViewManagerAbstract implements RNSVGTextManagerInterface { + static class TextViewManager extends TextViewManagerAbstract + implements RNSVGTextManagerInterface { TextViewManager() { super(SVGClass.RNSVGText); mDelegate = new RNSVGTextManagerDelegate(this); @@ -871,18 +863,18 @@ class RenderableViewManager extends VirtualViewManager private final ViewManagerDelegate mDelegate; - protected ViewManagerDelegate getDelegate(){ + protected ViewManagerDelegate getDelegate() { return mDelegate; } - TextViewManager(SVGClass svgClass) { - super(svgClass); - mDelegate = new RNSVGTextManagerDelegate(this); - } - + TextViewManager(SVGClass svgClass) { + super(svgClass); + mDelegate = new RNSVGTextManagerDelegate(this); + } } - static class TSpanViewManager extends TextViewManagerAbstract implements RNSVGTSpanManagerInterface { + static class TSpanViewManager extends TextViewManagerAbstract + implements RNSVGTSpanManagerInterface { TSpanViewManager() { super(SVGClass.RNSVGTSpan); mDelegate = new RNSVGTSpanManagerDelegate(this); @@ -895,18 +887,18 @@ class RenderableViewManager extends VirtualViewManager private final ViewManagerDelegate mDelegate; - protected ViewManagerDelegate getDelegate(){ + protected ViewManagerDelegate getDelegate() { return mDelegate; } - @ReactProp(name = "content") - public void setContent(TSpanView node, @Nullable String content) { - node.setContent(content); - } - + @ReactProp(name = "content") + public void setContent(TSpanView node, @Nullable String content) { + node.setContent(content); + } } - static class TextPathViewManager extends TextViewManagerAbstract implements RNSVGTextPathManagerInterface { + static class TextPathViewManager extends TextViewManagerAbstract + implements RNSVGTextPathManagerInterface { TextPathViewManager() { super(SVGClass.RNSVGTextPath); mDelegate = new RNSVGTextPathManagerDelegate(this); @@ -914,29 +906,29 @@ class RenderableViewManager extends VirtualViewManager TextPathViewManager(SVGClass svgClass) { super(svgClass); - mDelegate = new RNSVGTextPathManagerDelegate (this); + mDelegate = new RNSVGTextPathManagerDelegate(this); } private final ViewManagerDelegate mDelegate; - protected ViewManagerDelegate getDelegate(){ + protected ViewManagerDelegate getDelegate() { return mDelegate; } - @ReactProp(name = "href") - public void setHref(TextPathView node, String href) { - node.setHref(href); - } + @ReactProp(name = "href") + public void setHref(TextPathView node, String href) { + node.setHref(href); + } - @ReactProp(name = "startOffset") - public void setStartOffset(TextPathView node, Dynamic startOffset) { - node.setStartOffset(startOffset); - } + @ReactProp(name = "startOffset") + public void setStartOffset(TextPathView node, Dynamic startOffset) { + node.setStartOffset(startOffset); + } - @ReactProp(name = "method") - public void setMethod(TextPathView node, @Nullable String method) { - node.setMethod(method); - } + @ReactProp(name = "method") + public void setMethod(TextPathView node, @Nullable String method) { + node.setMethod(method); + } @Override public void setMidLine(TextPathView view, @Nullable String value) { @@ -944,9 +936,9 @@ class RenderableViewManager extends VirtualViewManager } @ReactProp(name = "spacing") - public void setSpacing(TextPathView node, @Nullable String spacing) { - node.setSpacing(spacing); - } + public void setSpacing(TextPathView node, @Nullable String spacing) { + node.setSpacing(spacing); + } @Override public void setStartOffset(TextPathView view, @Nullable String value) { @@ -954,17 +946,18 @@ class RenderableViewManager extends VirtualViewManager } @ReactProp(name = "side") - public void setSide(TextPathView node, @Nullable String side) { - node.setSide(side); - } - - @ReactProp(name = "midLine") - public void setSharp(TextPathView node, @Nullable String midLine) { - node.setSharp(midLine); - } + public void setSide(TextPathView node, @Nullable String side) { + node.setSide(side); } - static class ImageViewManager extends RenderableViewManager implements RNSVGImageManagerInterface { + @ReactProp(name = "midLine") + public void setSharp(TextPathView node, @Nullable String midLine) { + node.setSharp(midLine); + } + } + + static class ImageViewManager extends RenderableViewManager + implements RNSVGImageManagerInterface { ImageViewManager() { super(SVGClass.RNSVGImage); mDelegate = new RNSVGImageManagerDelegate(this); @@ -972,29 +965,29 @@ class RenderableViewManager extends VirtualViewManager private final ViewManagerDelegate mDelegate; - protected ViewManagerDelegate getDelegate(){ + protected ViewManagerDelegate getDelegate() { return mDelegate; } - @ReactProp(name = "x") - public void setX(ImageView node, Dynamic x) { - node.setX(x); - } + @ReactProp(name = "x") + public void setX(ImageView node, Dynamic x) { + node.setX(x); + } - @ReactProp(name = "y") - public void setY(ImageView node, Dynamic y) { - node.setY(y); - } + @ReactProp(name = "y") + public void setY(ImageView node, Dynamic y) { + node.setY(y); + } - @ReactProp(name = "width") - public void setWidth(ImageView node, Dynamic width) { - node.setWidth(width); - } + @ReactProp(name = "width") + public void setWidth(ImageView node, Dynamic width) { + node.setWidth(width); + } - @ReactProp(name = "height") - public void setHeight(ImageView node, Dynamic height) { - node.setHeight(height); - } + @ReactProp(name = "height") + public void setHeight(ImageView node, Dynamic height) { + node.setHeight(height); + } @Override public void setX(ImageView view, @Nullable String value) { @@ -1027,119 +1020,121 @@ class RenderableViewManager extends VirtualViewManager } @ReactProp(name = "src") - public void setSrc(ImageView node, @Nullable ReadableMap src) { - node.setSrc(src); - } - - - @ReactProp(name = "align") - public void setAlign(ImageView node, String align) { - node.setAlign(align); - } - - @ReactProp(name = "meetOrSlice") - public void setMeetOrSlice(ImageView node, int meetOrSlice) { - node.setMeetOrSlice(meetOrSlice); - } + public void setSrc(ImageView node, @Nullable ReadableMap src) { + node.setSrc(src); } - static class CircleViewManager extends RenderableViewManager implements RNSVGCircleManagerInterface { - CircleViewManager() { - super(SVGClass.RNSVGCircle); - mDelegate = new RNSVGCircleManagerDelegate(this); - } - - private final ViewManagerDelegate mDelegate; - - protected ViewManagerDelegate getDelegate(){ - return mDelegate; - } - - @ReactProp(name = "cx") - public void setCx(CircleView node, Dynamic cx) { - node.setCx(cx); - } - - @ReactProp(name = "cy") - public void setCy(CircleView node, Dynamic cy) { - node.setCy(cy); - } - - @ReactProp(name = "r") - public void setR(CircleView node, Dynamic r) { - node.setR(r); - } - - @Override - public void setCx(CircleView view, String value) { - view.setCx(value); - } - - @Override - public void setCy(CircleView view, String value) { - view.setCy(value); - } - - @Override - public void setR(CircleView view, String value) { - view.setR(value); - } + @ReactProp(name = "align") + public void setAlign(ImageView node, String align) { + node.setAlign(align); } - static class EllipseViewManager extends RenderableViewManager implements RNSVGEllipseManagerInterface { - EllipseViewManager() { - super(SVGClass.RNSVGEllipse); - mDelegate = new RNSVGEllipseManagerDelegate(this); - } + @ReactProp(name = "meetOrSlice") + public void setMeetOrSlice(ImageView node, int meetOrSlice) { + node.setMeetOrSlice(meetOrSlice); + } + } - private final ViewManagerDelegate mDelegate; - - protected ViewManagerDelegate getDelegate(){ - return mDelegate; - } - - @ReactProp(name = "cx") - public void setCx(EllipseView node, Dynamic cx) { - node.setCx(cx); - } - - @ReactProp(name = "cy") - public void setCy(EllipseView node, Dynamic cy) { - node.setCy(cy); - } - - @ReactProp(name = "rx") - public void setRx(EllipseView node, Dynamic rx) { - node.setRx(rx); - } - - @ReactProp(name = "ry") - public void setRy(EllipseView node, Dynamic ry) { - node.setRy(ry); - } - - @Override - public void setCx(EllipseView view, @Nullable String value) { - view.setCx(value); - } - - @Override - public void setCy(EllipseView view, @Nullable String value) { - view.setCy(value); - } - - @Override - public void setRx(EllipseView view, @Nullable String value) { - view.setRx(value); - } - - @Override - public void setRy(EllipseView view, @Nullable String value) { - view.setRy(value); - } + static class CircleViewManager extends RenderableViewManager + implements RNSVGCircleManagerInterface { + CircleViewManager() { + super(SVGClass.RNSVGCircle); + mDelegate = new RNSVGCircleManagerDelegate(this); } - static class LineViewManager extends RenderableViewManager implements RNSVGLineManagerInterface { + private final ViewManagerDelegate mDelegate; + + protected ViewManagerDelegate getDelegate() { + return mDelegate; + } + + @ReactProp(name = "cx") + public void setCx(CircleView node, Dynamic cx) { + node.setCx(cx); + } + + @ReactProp(name = "cy") + public void setCy(CircleView node, Dynamic cy) { + node.setCy(cy); + } + + @ReactProp(name = "r") + public void setR(CircleView node, Dynamic r) { + node.setR(r); + } + + @Override + public void setCx(CircleView view, String value) { + view.setCx(value); + } + + @Override + public void setCy(CircleView view, String value) { + view.setCy(value); + } + + @Override + public void setR(CircleView view, String value) { + view.setR(value); + } + } + + static class EllipseViewManager extends RenderableViewManager + implements RNSVGEllipseManagerInterface { + EllipseViewManager() { + super(SVGClass.RNSVGEllipse); + mDelegate = new RNSVGEllipseManagerDelegate(this); + } + + private final ViewManagerDelegate mDelegate; + + protected ViewManagerDelegate getDelegate() { + return mDelegate; + } + + @ReactProp(name = "cx") + public void setCx(EllipseView node, Dynamic cx) { + node.setCx(cx); + } + + @ReactProp(name = "cy") + public void setCy(EllipseView node, Dynamic cy) { + node.setCy(cy); + } + + @ReactProp(name = "rx") + public void setRx(EllipseView node, Dynamic rx) { + node.setRx(rx); + } + + @ReactProp(name = "ry") + public void setRy(EllipseView node, Dynamic ry) { + node.setRy(ry); + } + + @Override + public void setCx(EllipseView view, @Nullable String value) { + view.setCx(value); + } + + @Override + public void setCy(EllipseView view, @Nullable String value) { + view.setCy(value); + } + + @Override + public void setRx(EllipseView view, @Nullable String value) { + view.setRx(value); + } + + @Override + public void setRy(EllipseView view, @Nullable String value) { + view.setRy(value); + } + } + + static class LineViewManager extends RenderableViewManager + implements RNSVGLineManagerInterface { LineViewManager() { super(SVGClass.RNSVGLine); @@ -1148,29 +1143,29 @@ class RenderableViewManager extends VirtualViewManager private final ViewManagerDelegate mDelegate; - protected ViewManagerDelegate getDelegate(){ + protected ViewManagerDelegate getDelegate() { return mDelegate; } - @ReactProp(name = "x1") - public void setX1(LineView node, Dynamic x1) { - node.setX1(x1); - } + @ReactProp(name = "x1") + public void setX1(LineView node, Dynamic x1) { + node.setX1(x1); + } - @ReactProp(name = "y1") - public void setY1(LineView node, Dynamic y1) { - node.setY1(y1); - } + @ReactProp(name = "y1") + public void setY1(LineView node, Dynamic y1) { + node.setY1(y1); + } - @ReactProp(name = "x2") - public void setX2(LineView node, Dynamic x2) { - node.setX2(x2); - } + @ReactProp(name = "x2") + public void setX2(LineView node, Dynamic x2) { + node.setX2(x2); + } - @ReactProp(name = "y2") - public void setY2(LineView node, Dynamic y2) { - node.setY2(y2); - } + @ReactProp(name = "y2") + public void setY2(LineView node, Dynamic y2) { + node.setY2(y2); + } @Override public void setX1(LineView view, @Nullable String value) { @@ -1193,7 +1188,8 @@ class RenderableViewManager extends VirtualViewManager } } - static class RectViewManager extends RenderableViewManager implements RNSVGRectManagerInterface { + static class RectViewManager extends RenderableViewManager + implements RNSVGRectManagerInterface { RectViewManager() { super(SVGClass.RNSVGRect); @@ -1202,39 +1198,39 @@ class RenderableViewManager extends VirtualViewManager private final ViewManagerDelegate mDelegate; - protected ViewManagerDelegate getDelegate(){ + protected ViewManagerDelegate getDelegate() { return mDelegate; } - @ReactProp(name = "x") - public void setX(RectView node, Dynamic x) { - node.setX(x); - } + @ReactProp(name = "x") + public void setX(RectView node, Dynamic x) { + node.setX(x); + } - @ReactProp(name = "y") - public void setY(RectView node, Dynamic y) { - node.setY(y); - } + @ReactProp(name = "y") + public void setY(RectView node, Dynamic y) { + node.setY(y); + } - @ReactProp(name = "width") - public void setWidth(RectView node, Dynamic width) { - node.setWidth(width); - } + @ReactProp(name = "width") + public void setWidth(RectView node, Dynamic width) { + node.setWidth(width); + } - @ReactProp(name = "height") - public void setHeight(RectView node, Dynamic height) { - node.setHeight(height); - } + @ReactProp(name = "height") + public void setHeight(RectView node, Dynamic height) { + node.setHeight(height); + } - @ReactProp(name = "rx") - public void setRx(RectView node, Dynamic rx) { - node.setRx(rx); - } + @ReactProp(name = "rx") + public void setRx(RectView node, Dynamic rx) { + node.setRx(rx); + } - @ReactProp(name = "ry") - public void setRy(RectView node, Dynamic ry) { - node.setRy(ry); - } + @ReactProp(name = "ry") + public void setRy(RectView node, Dynamic ry) { + node.setRy(ry); + } @Override public void setX(RectView view, @Nullable String value) { @@ -1244,60 +1240,55 @@ class RenderableViewManager extends VirtualViewManager @Override public void setY(RectView view, @Nullable String value) { view.setY(value); - } @Override public void setRectheight(RectView view, @Nullable String value) { view.setHeight(value); - } @Override public void setRectwidth(RectView view, @Nullable String value) { view.setWidth(value); - } @Override public void setHeight(RectView view, @Nullable String value) { view.setHeight(value); - } @Override public void setWidth(RectView view, @Nullable String value) { view.setWidth(value); - } @Override public void setRx(RectView view, @Nullable String value) { view.setRx(value); - } @Override public void setRy(RectView view, @Nullable String value) { view.setRy(value); - } } - static class ClipPathViewManager extends GroupViewManagerAbstract implements RNSVGClipPathManagerInterface{ - ClipPathViewManager() { - super(SVGClass.RNSVGClipPath); - mDelegate = new RNSVGClipPathManagerDelegate(this); - } - - private final ViewManagerDelegate mDelegate; - - protected ViewManagerDelegate getDelegate(){ - return mDelegate; - } + static class ClipPathViewManager extends GroupViewManagerAbstract + implements RNSVGClipPathManagerInterface { + ClipPathViewManager() { + super(SVGClass.RNSVGClipPath); + mDelegate = new RNSVGClipPathManagerDelegate(this); } - static class DefsViewManager extends VirtualViewManager implements RNSVGDefsManagerInterface { + private final ViewManagerDelegate mDelegate; + + protected ViewManagerDelegate getDelegate() { + return mDelegate; + } + } + + static class DefsViewManager extends VirtualViewManager + implements RNSVGDefsManagerInterface { DefsViewManager() { super(SVGClass.RNSVGDefs); @@ -1306,12 +1297,13 @@ class RenderableViewManager extends VirtualViewManager private final ViewManagerDelegate mDelegate; - protected ViewManagerDelegate getDelegate(){ + protected ViewManagerDelegate getDelegate() { return mDelegate; } - } + } - static class UseViewManager extends RenderableViewManager implements RNSVGUseManagerInterface { + static class UseViewManager extends RenderableViewManager + implements RNSVGUseManagerInterface { UseViewManager() { super(SVGClass.RNSVGUse); @@ -1320,14 +1312,14 @@ class RenderableViewManager extends VirtualViewManager private final ViewManagerDelegate mDelegate; - protected ViewManagerDelegate getDelegate(){ + protected ViewManagerDelegate getDelegate() { return mDelegate; } - @ReactProp(name = "href") - public void setHref(UseView node, String href) { - node.setHref(href); - } + @ReactProp(name = "href") + public void setHref(UseView node, String href) { + node.setHref(href); + } @Override public void setX(UseView view, @Nullable String value) { @@ -1360,27 +1352,28 @@ class RenderableViewManager extends VirtualViewManager } @ReactProp(name = "x") - public void setX(UseView node, Dynamic x) { - node.setX(x); - } - - @ReactProp(name = "y") - public void setY(UseView node, Dynamic y) { - node.setY(y); - } - - @ReactProp(name = "width") - public void setWidth(UseView node, Dynamic width) { - node.setWidth(width); - } - - @ReactProp(name = "height") - public void setHeight(UseView node, Dynamic height) { - node.setHeight(height); - } + public void setX(UseView node, Dynamic x) { + node.setX(x); } - static class SymbolManager extends GroupViewManagerAbstract implements RNSVGSymbolManagerInterface { + @ReactProp(name = "y") + public void setY(UseView node, Dynamic y) { + node.setY(y); + } + + @ReactProp(name = "width") + public void setWidth(UseView node, Dynamic width) { + node.setWidth(width); + } + + @ReactProp(name = "height") + public void setHeight(UseView node, Dynamic height) { + node.setHeight(height); + } + } + + static class SymbolManager extends GroupViewManagerAbstract + implements RNSVGSymbolManagerInterface { SymbolManager() { super(SVGClass.RNSVGSymbol); mDelegate = new RNSVGSymbolManagerDelegate(this); @@ -1388,42 +1381,43 @@ class RenderableViewManager extends VirtualViewManager private final ViewManagerDelegate mDelegate; - protected ViewManagerDelegate getDelegate(){ + protected ViewManagerDelegate getDelegate() { return mDelegate; } - @ReactProp(name = "minX") - public void setMinX(SymbolView node, float minX) { - node.setMinX(minX); - } - - @ReactProp(name = "minY") - public void setMinY(SymbolView node, float minY) { - node.setMinY(minY); - } - - @ReactProp(name = "vbWidth") - public void setVbWidth(SymbolView node, float vbWidth) { - node.setVbWidth(vbWidth); - } - - @ReactProp(name = "vbHeight") - public void setVbHeight(SymbolView node, float vbHeight) { - node.setVbHeight(vbHeight); - } - - @ReactProp(name = "align") - public void setAlign(SymbolView node, String align) { - node.setAlign(align); - } - - @ReactProp(name = "meetOrSlice") - public void setMeetOrSlice(SymbolView node, int meetOrSlice) { - node.setMeetOrSlice(meetOrSlice); - } + @ReactProp(name = "minX") + public void setMinX(SymbolView node, float minX) { + node.setMinX(minX); } - static class PatternManager extends GroupViewManagerAbstract implements RNSVGPatternManagerInterface { + @ReactProp(name = "minY") + public void setMinY(SymbolView node, float minY) { + node.setMinY(minY); + } + + @ReactProp(name = "vbWidth") + public void setVbWidth(SymbolView node, float vbWidth) { + node.setVbWidth(vbWidth); + } + + @ReactProp(name = "vbHeight") + public void setVbHeight(SymbolView node, float vbHeight) { + node.setVbHeight(vbHeight); + } + + @ReactProp(name = "align") + public void setAlign(SymbolView node, String align) { + node.setAlign(align); + } + + @ReactProp(name = "meetOrSlice") + public void setMeetOrSlice(SymbolView node, int meetOrSlice) { + node.setMeetOrSlice(meetOrSlice); + } + } + + static class PatternManager extends GroupViewManagerAbstract + implements RNSVGPatternManagerInterface { PatternManager() { super(SVGClass.RNSVGPattern); mDelegate = new RNSVGPatternManagerDelegate(this); @@ -1431,33 +1425,33 @@ class RenderableViewManager extends VirtualViewManager private final ViewManagerDelegate mDelegate; - protected ViewManagerDelegate getDelegate(){ + protected ViewManagerDelegate getDelegate() { return mDelegate; } - @ReactProp(name = "x") - public void setX(PatternView node, Dynamic x) { - node.setX(x); - } + @ReactProp(name = "x") + public void setX(PatternView node, Dynamic x) { + node.setX(x); + } - @ReactProp(name = "y") - public void setY(PatternView node, Dynamic y) { - node.setY(y); - } + @ReactProp(name = "y") + public void setY(PatternView node, Dynamic y) { + node.setY(y); + } - @ReactProp(name = "width") - public void setWidth(PatternView node, Dynamic width) { - node.setWidth(width); - } + @ReactProp(name = "width") + public void setWidth(PatternView node, Dynamic width) { + node.setWidth(width); + } - @ReactProp(name = "height") - public void setHeight(PatternView node, Dynamic height) { - node.setHeight(height); - } + @ReactProp(name = "height") + public void setHeight(PatternView node, Dynamic height) { + node.setHeight(height); + } @Override public void setX(PatternView view, @Nullable String value) { - view.setX(value); + view.setX(value); } @Override @@ -1486,52 +1480,53 @@ class RenderableViewManager extends VirtualViewManager } @ReactProp(name = "patternUnits") - public void setPatternUnits(PatternView node, int patternUnits) { - node.setPatternUnits(patternUnits); - } - - @ReactProp(name = "patternContentUnits") - public void setPatternContentUnits(PatternView node, int patternContentUnits) { - node.setPatternContentUnits(patternContentUnits); - } - - @ReactProp(name = "patternTransform") - public void setPatternTransform(PatternView node, @Nullable ReadableArray matrixArray) { - node.setPatternTransform(matrixArray); - } - - @ReactProp(name = "minX") - public void setMinX(PatternView node, float minX) { - node.setMinX(minX); - } - - @ReactProp(name = "minY") - public void setMinY(PatternView node, float minY) { - node.setMinY(minY); - } - - @ReactProp(name = "vbWidth") - public void setVbWidth(PatternView node, float vbWidth) { - node.setVbWidth(vbWidth); - } - - @ReactProp(name = "vbHeight") - public void setVbHeight(PatternView node, float vbHeight) { - node.setVbHeight(vbHeight); - } - - @ReactProp(name = "align") - public void setAlign(PatternView node, String align) { - node.setAlign(align); - } - - @ReactProp(name = "meetOrSlice") - public void setMeetOrSlice(PatternView node, int meetOrSlice) { - node.setMeetOrSlice(meetOrSlice); - } + public void setPatternUnits(PatternView node, int patternUnits) { + node.setPatternUnits(patternUnits); } - static class MaskManager extends GroupViewManagerAbstract implements RNSVGMaskManagerInterface { + @ReactProp(name = "patternContentUnits") + public void setPatternContentUnits(PatternView node, int patternContentUnits) { + node.setPatternContentUnits(patternContentUnits); + } + + @ReactProp(name = "patternTransform") + public void setPatternTransform(PatternView node, @Nullable ReadableArray matrixArray) { + node.setPatternTransform(matrixArray); + } + + @ReactProp(name = "minX") + public void setMinX(PatternView node, float minX) { + node.setMinX(minX); + } + + @ReactProp(name = "minY") + public void setMinY(PatternView node, float minY) { + node.setMinY(minY); + } + + @ReactProp(name = "vbWidth") + public void setVbWidth(PatternView node, float vbWidth) { + node.setVbWidth(vbWidth); + } + + @ReactProp(name = "vbHeight") + public void setVbHeight(PatternView node, float vbHeight) { + node.setVbHeight(vbHeight); + } + + @ReactProp(name = "align") + public void setAlign(PatternView node, String align) { + node.setAlign(align); + } + + @ReactProp(name = "meetOrSlice") + public void setMeetOrSlice(PatternView node, int meetOrSlice) { + node.setMeetOrSlice(meetOrSlice); + } + } + + static class MaskManager extends GroupViewManagerAbstract + implements RNSVGMaskManagerInterface { MaskManager() { super(SVGClass.RNSVGMask); mDelegate = new RNSVGMaskManagerDelegate(this); @@ -1539,29 +1534,29 @@ class RenderableViewManager extends VirtualViewManager private final ViewManagerDelegate mDelegate; - protected ViewManagerDelegate getDelegate(){ + protected ViewManagerDelegate getDelegate() { return mDelegate; } - @ReactProp(name = "x") - public void setX(MaskView node, Dynamic x) { - node.setX(x); - } + @ReactProp(name = "x") + public void setX(MaskView node, Dynamic x) { + node.setX(x); + } - @ReactProp(name = "y") - public void setY(MaskView node, Dynamic y) { - node.setY(y); - } + @ReactProp(name = "y") + public void setY(MaskView node, Dynamic y) { + node.setY(y); + } - @ReactProp(name = "width") - public void setWidth(MaskView node, Dynamic width) { - node.setWidth(width); - } + @ReactProp(name = "width") + public void setWidth(MaskView node, Dynamic width) { + node.setWidth(width); + } - @ReactProp(name = "height") - public void setHeight(MaskView node, Dynamic height) { - node.setHeight(height); - } + @ReactProp(name = "height") + public void setHeight(MaskView node, Dynamic height) { + node.setHeight(height); + } @Override public void setX(MaskView view, @Nullable String value) { @@ -1594,84 +1589,87 @@ class RenderableViewManager extends VirtualViewManager } @ReactProp(name = "maskUnits") - public void setMaskUnits(MaskView node, int maskUnits) { - node.setMaskUnits(maskUnits); - } - - @ReactProp(name = "maskContentUnits") - public void setMaskContentUnits(MaskView node, int maskContentUnits) { - node.setMaskContentUnits(maskContentUnits); - } - - @ReactProp(name = "maskTransform") - public void setMaskTransform(MaskView node, @Nullable ReadableArray matrixArray) { - node.setMaskTransform(matrixArray); - } + public void setMaskUnits(MaskView node, int maskUnits) { + node.setMaskUnits(maskUnits); } - static class ForeignObjectManager extends GroupViewManagerAbstract implements RNSVGForeignObjectManagerInterface { - ForeignObjectManager() { - super(SVGClass.RNSVGForeignObject); - mDelegate = new RNSVGForeignObjectManagerDelegate(this); - } + @ReactProp(name = "maskContentUnits") + public void setMaskContentUnits(MaskView node, int maskContentUnits) { + node.setMaskContentUnits(maskContentUnits); + } - private final ViewManagerDelegate mDelegate; + @ReactProp(name = "maskTransform") + public void setMaskTransform(MaskView node, @Nullable ReadableArray matrixArray) { + node.setMaskTransform(matrixArray); + } + } - protected ViewManagerDelegate getDelegate(){ - return mDelegate; - } - @ReactProp(name = "x") - public void setX(ForeignObjectView node, Dynamic x) { - node.setX(x); - } + static class ForeignObjectManager extends GroupViewManagerAbstract + implements RNSVGForeignObjectManagerInterface { + ForeignObjectManager() { + super(SVGClass.RNSVGForeignObject); + mDelegate = new RNSVGForeignObjectManagerDelegate(this); + } - @ReactProp(name = "y") - public void setY(ForeignObjectView node, Dynamic y) { - node.setY(y); - } + private final ViewManagerDelegate mDelegate; - @ReactProp(name = "width") - public void setWidth(ForeignObjectView node, Dynamic width) { - node.setWidth(width); - } + protected ViewManagerDelegate getDelegate() { + return mDelegate; + } - @ReactProp(name = "height") - public void setHeight(ForeignObjectView node, Dynamic height) { - node.setHeight(height); - } + @ReactProp(name = "x") + public void setX(ForeignObjectView node, Dynamic x) { + node.setX(x); + } - @Override - public void setX(ForeignObjectView view, @Nullable String value) { - view.setX(value); - } + @ReactProp(name = "y") + public void setY(ForeignObjectView node, Dynamic y) { + node.setY(y); + } - @Override - public void setY(ForeignObjectView view, @Nullable String value) { - view.setY(value); - } + @ReactProp(name = "width") + public void setWidth(ForeignObjectView node, Dynamic width) { + node.setWidth(width); + } - @Override - public void setForeignObjectheight(ForeignObjectView view, @Nullable String value) { - view.setHeight(value); - } + @ReactProp(name = "height") + public void setHeight(ForeignObjectView node, Dynamic height) { + node.setHeight(height); + } - @Override - public void setForeignObjectwidth(ForeignObjectView view, @Nullable String value) { - view.setWidth(value); - } + @Override + public void setX(ForeignObjectView view, @Nullable String value) { + view.setX(value); + } - @Override - public void setHeight(ForeignObjectView view, @Nullable String value) { - view.setHeight(value); - } + @Override + public void setY(ForeignObjectView view, @Nullable String value) { + view.setY(value); + } - @Override - public void setWidth(ForeignObjectView view, @Nullable String value) { - view.setWidth(value); - } - } + @Override + public void setForeignObjectheight(ForeignObjectView view, @Nullable String value) { + view.setHeight(value); + } - static class MarkerManager extends GroupViewManagerAbstract implements RNSVGMarkerManagerInterface { + @Override + public void setForeignObjectwidth(ForeignObjectView view, @Nullable String value) { + view.setWidth(value); + } + + @Override + public void setHeight(ForeignObjectView view, @Nullable String value) { + view.setHeight(value); + } + + @Override + public void setWidth(ForeignObjectView view, @Nullable String value) { + view.setWidth(value); + } + } + + static class MarkerManager extends GroupViewManagerAbstract + implements RNSVGMarkerManagerInterface { MarkerManager() { super(SVGClass.RNSVGMarker); mDelegate = new RNSVGMarkerManagerDelegate(this); @@ -1679,29 +1677,29 @@ class RenderableViewManager extends VirtualViewManager private final ViewManagerDelegate mDelegate; - protected ViewManagerDelegate getDelegate(){ + protected ViewManagerDelegate getDelegate() { return mDelegate; } - @ReactProp(name = "refX") - public void setRefX(MarkerView node, Dynamic refX) { - node.setRefX(refX); - } + @ReactProp(name = "refX") + public void setRefX(MarkerView node, Dynamic refX) { + node.setRefX(refX); + } - @ReactProp(name = "refY") - public void setRefY(MarkerView node, Dynamic refY) { - node.setRefY(refY); - } + @ReactProp(name = "refY") + public void setRefY(MarkerView node, Dynamic refY) { + node.setRefY(refY); + } - @ReactProp(name = "markerWidth") - public void setMarkerWidth(MarkerView node, Dynamic markerWidth) { - node.setMarkerWidth(markerWidth); - } + @ReactProp(name = "markerWidth") + public void setMarkerWidth(MarkerView node, Dynamic markerWidth) { + node.setMarkerWidth(markerWidth); + } - @ReactProp(name = "markerHeight") - public void setMarkerHeight(MarkerView node, Dynamic markerHeight) { - node.setMarkerHeight(markerHeight); - } + @ReactProp(name = "markerHeight") + public void setMarkerHeight(MarkerView node, Dynamic markerHeight) { + node.setMarkerHeight(markerHeight); + } @Override public void setRefX(MarkerView view, @Nullable String value) { @@ -1724,47 +1722,48 @@ class RenderableViewManager extends VirtualViewManager } @ReactProp(name = "markerUnits") - public void setMarkerUnits(MarkerView node, String markerUnits) { - node.setMarkerUnits(markerUnits); - } - - @ReactProp(name = "orient") - public void setOrient(MarkerView node, String orient) { - node.setOrient(orient); - } - - @ReactProp(name = "minX") - public void setMinX(MarkerView node, float minX) { - node.setMinX(minX); - } - - @ReactProp(name = "minY") - public void setMinY(MarkerView node, float minY) { - node.setMinY(minY); - } - - @ReactProp(name = "vbWidth") - public void setVbWidth(MarkerView node, float vbWidth) { - node.setVbWidth(vbWidth); - } - - @ReactProp(name = "vbHeight") - public void setVbHeight(MarkerView node, float vbHeight) { - node.setVbHeight(vbHeight); - } - - @ReactProp(name = "align") - public void setAlign(MarkerView node, String align) { - node.setAlign(align); - } - - @ReactProp(name = "meetOrSlice") - public void setMeetOrSlice(MarkerView node, int meetOrSlice) { - node.setMeetOrSlice(meetOrSlice); - } + public void setMarkerUnits(MarkerView node, String markerUnits) { + node.setMarkerUnits(markerUnits); } - static class LinearGradientManager extends VirtualViewManager implements RNSVGLinearGradientManagerInterface { + @ReactProp(name = "orient") + public void setOrient(MarkerView node, String orient) { + node.setOrient(orient); + } + + @ReactProp(name = "minX") + public void setMinX(MarkerView node, float minX) { + node.setMinX(minX); + } + + @ReactProp(name = "minY") + public void setMinY(MarkerView node, float minY) { + node.setMinY(minY); + } + + @ReactProp(name = "vbWidth") + public void setVbWidth(MarkerView node, float vbWidth) { + node.setVbWidth(vbWidth); + } + + @ReactProp(name = "vbHeight") + public void setVbHeight(MarkerView node, float vbHeight) { + node.setVbHeight(vbHeight); + } + + @ReactProp(name = "align") + public void setAlign(MarkerView node, String align) { + node.setAlign(align); + } + + @ReactProp(name = "meetOrSlice") + public void setMeetOrSlice(MarkerView node, int meetOrSlice) { + node.setMeetOrSlice(meetOrSlice); + } + } + + static class LinearGradientManager extends VirtualViewManager + implements RNSVGLinearGradientManagerInterface { LinearGradientManager() { super(SVGClass.RNSVGLinearGradient); @@ -1773,30 +1772,29 @@ class RenderableViewManager extends VirtualViewManager private final ViewManagerDelegate mDelegate; - protected ViewManagerDelegate getDelegate(){ + protected ViewManagerDelegate getDelegate() { return mDelegate; } + @ReactProp(name = "x1") + public void setX1(LinearGradientView node, Dynamic x1) { + node.setX1(x1); + } - @ReactProp(name = "x1") - public void setX1(LinearGradientView node, Dynamic x1) { - node.setX1(x1); - } + @ReactProp(name = "y1") + public void setY1(LinearGradientView node, Dynamic y1) { + node.setY1(y1); + } - @ReactProp(name = "y1") - public void setY1(LinearGradientView node, Dynamic y1) { - node.setY1(y1); - } + @ReactProp(name = "x2") + public void setX2(LinearGradientView node, Dynamic x2) { + node.setX2(x2); + } - @ReactProp(name = "x2") - public void setX2(LinearGradientView node, Dynamic x2) { - node.setX2(x2); - } - - @ReactProp(name = "y2") - public void setY2(LinearGradientView node, Dynamic y2) { - node.setY2(y2); - } + @ReactProp(name = "y2") + public void setY2(LinearGradientView node, Dynamic y2) { + node.setY2(y2); + } @Override public void setX1(LinearGradientView view, @Nullable String value) { @@ -1819,22 +1817,23 @@ class RenderableViewManager extends VirtualViewManager } @ReactProp(name = "gradient") - public void setGradient(LinearGradientView node, ReadableArray gradient) { - node.setGradient(gradient); - } - - @ReactProp(name = "gradientUnits") - public void setGradientUnits(LinearGradientView node, int gradientUnits) { - node.setGradientUnits(gradientUnits); - } - - @ReactProp(name = "gradientTransform") - public void setGradientTransform(LinearGradientView node, @Nullable ReadableArray matrixArray) { - node.setGradientTransform(matrixArray); - } + public void setGradient(LinearGradientView node, ReadableArray gradient) { + node.setGradient(gradient); } - static class RadialGradientManager extends VirtualViewManager implements RNSVGRadialGradientManagerInterface { + @ReactProp(name = "gradientUnits") + public void setGradientUnits(LinearGradientView node, int gradientUnits) { + node.setGradientUnits(gradientUnits); + } + + @ReactProp(name = "gradientTransform") + public void setGradientTransform(LinearGradientView node, @Nullable ReadableArray matrixArray) { + node.setGradientTransform(matrixArray); + } + } + + static class RadialGradientManager extends VirtualViewManager + implements RNSVGRadialGradientManagerInterface { RadialGradientManager() { super(SVGClass.RNSVGRadialGradient); @@ -1843,39 +1842,39 @@ class RenderableViewManager extends VirtualViewManager private final ViewManagerDelegate mDelegate; - protected ViewManagerDelegate getDelegate(){ + protected ViewManagerDelegate getDelegate() { return mDelegate; } - @ReactProp(name = "fx") - public void setFx(RadialGradientView node, Dynamic fx) { - node.setFx(fx); - } + @ReactProp(name = "fx") + public void setFx(RadialGradientView node, Dynamic fx) { + node.setFx(fx); + } - @ReactProp(name = "fy") - public void setFy(RadialGradientView node, Dynamic fy) { - node.setFy(fy); - } + @ReactProp(name = "fy") + public void setFy(RadialGradientView node, Dynamic fy) { + node.setFy(fy); + } - @ReactProp(name = "rx") - public void setRx(RadialGradientView node, Dynamic rx) { - node.setRx(rx); - } + @ReactProp(name = "rx") + public void setRx(RadialGradientView node, Dynamic rx) { + node.setRx(rx); + } - @ReactProp(name = "ry") - public void setRy(RadialGradientView node, Dynamic ry) { - node.setRy(ry); - } + @ReactProp(name = "ry") + public void setRy(RadialGradientView node, Dynamic ry) { + node.setRy(ry); + } - @ReactProp(name = "cx") - public void setCx(RadialGradientView node, Dynamic cx) { - node.setCx(cx); - } + @ReactProp(name = "cx") + public void setCx(RadialGradientView node, Dynamic cx) { + node.setCx(cx); + } - @ReactProp(name = "cy") - public void setCy(RadialGradientView node, Dynamic cy) { - node.setCy(cy); - } + @ReactProp(name = "cy") + public void setCy(RadialGradientView node, Dynamic cy) { + node.setCy(cy); + } @Override public void setFx(RadialGradientView view, @Nullable String value) { @@ -1908,97 +1907,95 @@ class RenderableViewManager extends VirtualViewManager } @ReactProp(name = "gradient") - public void setGradient(RadialGradientView node, ReadableArray gradient) { - node.setGradient(gradient); - } - - @ReactProp(name = "gradientUnits") - public void setGradientUnits(RadialGradientView node, int gradientUnits) { - node.setGradientUnits(gradientUnits); - } - - @ReactProp(name = "gradientTransform") - public void setGradientTransform(RadialGradientView node, @Nullable ReadableArray matrixArray) { - node.setGradientTransform(matrixArray); - } + public void setGradient(RadialGradientView node, ReadableArray gradient) { + node.setGradient(gradient); } + @ReactProp(name = "gradientUnits") + public void setGradientUnits(RadialGradientView node, int gradientUnits) { + node.setGradientUnits(gradientUnits); + } + + @ReactProp(name = "gradientTransform") + public void setGradientTransform(RadialGradientView node, @Nullable ReadableArray matrixArray) { + node.setGradientTransform(matrixArray); + } + } + @ReactProp(name = "fill") - public void setFill(T node, @Nullable Dynamic fill) { - node.setFill(fill); - } + public void setFill(T node, @Nullable Dynamic fill) { + node.setFill(fill); + } public void setFill(T view, @Nullable ReadableMap value) { view.setFill(value); } - @ReactProp(name = "fillOpacity", defaultFloat = 1f) - public void setFillOpacity(T node, float fillOpacity) { - node.setFillOpacity(fillOpacity); - } - - @ReactProp(name = "fillRule", defaultInt = FILL_RULE_NONZERO) - public void setFillRule(T node, int fillRule) { - node.setFillRule(fillRule); - } - - - @ReactProp(name = "stroke") - public void setStroke(T node, @Nullable Dynamic strokeColors) { - node.setStroke(strokeColors); - } - - public void setStroke(T view, @Nullable ReadableMap value) { - view.setStroke(value); + @ReactProp(name = "fillOpacity", defaultFloat = 1f) + public void setFillOpacity(T node, float fillOpacity) { + node.setFillOpacity(fillOpacity); } - @ReactProp(name = "strokeOpacity", defaultFloat = 1f) - public void setStrokeOpacity(T node, float strokeOpacity) { - node.setStrokeOpacity(strokeOpacity); - } + @ReactProp(name = "fillRule", defaultInt = FILL_RULE_NONZERO) + public void setFillRule(T node, int fillRule) { + node.setFillRule(fillRule); + } - @ReactProp(name = "strokeDasharray") - public void setStrokeDasharray(T node, @Nullable ReadableArray strokeDasharray) { - node.setStrokeDasharray(strokeDasharray); - } + @ReactProp(name = "stroke") + public void setStroke(T node, @Nullable Dynamic strokeColors) { + node.setStroke(strokeColors); + } - @ReactProp(name = "strokeDashoffset") - public void setStrokeDashoffset(T node, float strokeDashoffset) { - node.setStrokeDashoffset(strokeDashoffset); - } + public void setStroke(T view, @Nullable ReadableMap value) { + view.setStroke(value); + } - @ReactProp(name = "strokeWidth") - public void setStrokeWidth(T node, Dynamic strokeWidth) { - node.setStrokeWidth(strokeWidth); - } + @ReactProp(name = "strokeOpacity", defaultFloat = 1f) + public void setStrokeOpacity(T node, float strokeOpacity) { + node.setStrokeOpacity(strokeOpacity); + } + + @ReactProp(name = "strokeDasharray") + public void setStrokeDasharray(T node, @Nullable ReadableArray strokeDasharray) { + node.setStrokeDasharray(strokeDasharray); + } + + @ReactProp(name = "strokeDashoffset") + public void setStrokeDashoffset(T node, float strokeDashoffset) { + node.setStrokeDashoffset(strokeDashoffset); + } + + @ReactProp(name = "strokeWidth") + public void setStrokeWidth(T node, Dynamic strokeWidth) { + node.setStrokeWidth(strokeWidth); + } public void setStrokeWidth(T view, @Nullable String value) { view.setStrokeWidth(value); } - @ReactProp(name = "strokeMiterlimit", defaultFloat = 4f) - public void setStrokeMiterlimit(T node, float strokeMiterlimit) { - node.setStrokeMiterlimit(strokeMiterlimit); - } + @ReactProp(name = "strokeMiterlimit", defaultFloat = 4f) + public void setStrokeMiterlimit(T node, float strokeMiterlimit) { + node.setStrokeMiterlimit(strokeMiterlimit); + } - @ReactProp(name = "strokeLinecap", defaultInt = CAP_ROUND) - public void setStrokeLinecap(T node, int strokeLinecap) { - node.setStrokeLinecap(strokeLinecap); - } + @ReactProp(name = "strokeLinecap", defaultInt = CAP_ROUND) + public void setStrokeLinecap(T node, int strokeLinecap) { + node.setStrokeLinecap(strokeLinecap); + } - @ReactProp(name = "strokeLinejoin", defaultInt = JOIN_ROUND) - public void setStrokeLinejoin(T node, int strokeLinejoin) { - node.setStrokeLinejoin(strokeLinejoin); - } + @ReactProp(name = "strokeLinejoin", defaultInt = JOIN_ROUND) + public void setStrokeLinejoin(T node, int strokeLinejoin) { + node.setStrokeLinejoin(strokeLinejoin); + } - @ReactProp(name = "vectorEffect") - public void setVectorEffect(T node, int vectorEffect) { - node.setVectorEffect(vectorEffect); - } - - @ReactProp(name = "propList") - public void setPropList(T node, @Nullable ReadableArray propList) { - node.setPropList(propList); - } + @ReactProp(name = "vectorEffect") + public void setVectorEffect(T node, int vectorEffect) { + node.setVectorEffect(vectorEffect); + } + @ReactProp(name = "propList") + public void setPropList(T node, @Nullable ReadableArray propList) { + node.setPropList(propList); + } } diff --git a/android/src/main/java/com/horcrux/svg/SVGLength.java b/android/src/main/java/com/horcrux/svg/SVGLength.java index b2203bc6..85ad120b 100644 --- a/android/src/main/java/com/horcrux/svg/SVGLength.java +++ b/android/src/main/java/com/horcrux/svg/SVGLength.java @@ -2,7 +2,6 @@ package com.horcrux.svg; import com.facebook.react.bridge.Dynamic; import com.facebook.react.bridge.ReadableArray; - import java.util.ArrayList; class SVGLength { @@ -121,26 +120,29 @@ class SVGLength { static ArrayList arrayFrom(Dynamic dynamic) { switch (dynamic.getType()) { - case Number: { - ArrayList list = new ArrayList<>(1); - list.add(new SVGLength(dynamic.asDouble())); - return list; - } - case Array: { - ReadableArray arr = dynamic.asArray(); - int size = arr.size(); - ArrayList list = new ArrayList<>(size); - for (int i = 0; i < size; i++) { - Dynamic val = arr.getDynamic(i); - list.add(from(val)); + case Number: + { + ArrayList list = new ArrayList<>(1); + list.add(new SVGLength(dynamic.asDouble())); + return list; + } + case Array: + { + ReadableArray arr = dynamic.asArray(); + int size = arr.size(); + ArrayList list = new ArrayList<>(size); + for (int i = 0; i < size; i++) { + Dynamic val = arr.getDynamic(i); + list.add(from(val)); + } + return list; + } + case String: + { + ArrayList list = new ArrayList<>(1); + list.add(new SVGLength(dynamic.asString())); + return list; } - return list; - } - case String: { - ArrayList list = new ArrayList<>(1); - list.add(new SVGLength(dynamic.asString())); - return list; - } default: return null; } diff --git a/android/src/main/java/com/horcrux/svg/SvgPackage.java b/android/src/main/java/com/horcrux/svg/SvgPackage.java index bc3a09b8..04a60ae6 100644 --- a/android/src/main/java/com/horcrux/svg/SvgPackage.java +++ b/android/src/main/java/com/horcrux/svg/SvgPackage.java @@ -6,71 +6,66 @@ * LICENSE file in the root directory of this source tree. */ - package com.horcrux.svg; +import static com.horcrux.svg.RenderableViewManager.*; + import com.facebook.react.ReactPackage; import com.facebook.react.bridge.JavaScriptModule; import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.uimanager.ViewManager; import com.facebook.soloader.SoLoader; - import java.util.Arrays; import java.util.Collections; import java.util.List; - import javax.annotation.Nonnull; -import static com.horcrux.svg.RenderableViewManager.*; - public class SvgPackage implements ReactPackage { - @Nonnull - @Override - public List createViewManagers(@Nonnull ReactApplicationContext reactContext) { - return Arrays.asList( - new GroupViewManager(), - new PathViewManager(), - new CircleViewManager(), - new EllipseViewManager(), - new LineViewManager(), - new RectViewManager(), - new TextViewManager(), - new TSpanViewManager(), - new TextPathViewManager(), - new ImageViewManager(), - new ClipPathViewManager(), - new DefsViewManager(), - new UseViewManager(), - new SymbolManager(), - new LinearGradientManager(), - new RadialGradientManager(), - new PatternManager(), - new MaskManager(), - new ForeignObjectManager(), - new MarkerManager(), - new SvgViewManager()); - } + @Nonnull + @Override + public List createViewManagers(@Nonnull ReactApplicationContext reactContext) { + return Arrays.asList( + new GroupViewManager(), + new PathViewManager(), + new CircleViewManager(), + new EllipseViewManager(), + new LineViewManager(), + new RectViewManager(), + new TextViewManager(), + new TSpanViewManager(), + new TextPathViewManager(), + new ImageViewManager(), + new ClipPathViewManager(), + new DefsViewManager(), + new UseViewManager(), + new SymbolManager(), + new LinearGradientManager(), + new RadialGradientManager(), + new PatternManager(), + new MaskManager(), + new ForeignObjectManager(), + new MarkerManager(), + new SvgViewManager()); + } - @Nonnull - @Override - public List createNativeModules(@Nonnull ReactApplicationContext reactContext) { - if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { - // For Fabric, we load c++ native library here, this triggers svg's Fabric - // component registration which is necessary in order to avoid asking users - // to manually add init calls in their application code. - // This should no longer be needed if RN's autolink mechanism has Fabric support - SoLoader.loadLibrary("rnsvg_modules"); - } - return Arrays.asList( - new SvgViewModule(reactContext), - new RNSVGRenderableManager(reactContext) - ); + @Nonnull + @Override + public List createNativeModules(@Nonnull ReactApplicationContext reactContext) { + if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { + // For Fabric, we load c++ native library here, this triggers svg's Fabric + // component registration which is necessary in order to avoid asking users + // to manually add init calls in their application code. + // This should no longer be needed if RN's autolink mechanism has Fabric support + SoLoader.loadLibrary("rnsvg_modules"); } + return Arrays.asList( + new SvgViewModule(reactContext), new RNSVGRenderableManager(reactContext)); + } - @SuppressWarnings("unused") - public List> createJSModules() { - return Collections.emptyList(); - } + @SuppressWarnings("unused") + public List> createJSModules() { + return Collections.emptyList(); + } } diff --git a/android/src/main/java/com/horcrux/svg/SvgView.java b/android/src/main/java/com/horcrux/svg/SvgView.java index 9c35cf1b..f4bce9d3 100644 --- a/android/src/main/java/com/horcrux/svg/SvgView.java +++ b/android/src/main/java/com/horcrux/svg/SvgView.java @@ -6,7 +6,6 @@ * LICENSE file in the root directory of this source tree. */ - package com.horcrux.svg; import android.annotation.SuppressLint; @@ -20,9 +19,6 @@ import android.graphics.Typeface; import android.util.Base64; import android.view.View; import android.view.ViewParent; - -import androidx.annotation.NonNull; - import com.facebook.react.bridge.ColorPropConverter; import com.facebook.react.bridge.Dynamic; import com.facebook.react.bridge.ReactContext; @@ -30,207 +26,203 @@ import com.facebook.react.uimanager.DisplayMetricsHolder; import com.facebook.react.uimanager.ReactCompoundView; import com.facebook.react.uimanager.ReactCompoundViewGroup; import com.facebook.react.uimanager.annotations.ReactProp; -import com.facebook.react.views.view.ReactViewGroup; - import java.io.ByteArrayOutputStream; import java.util.HashMap; import java.util.Map; - import javax.annotation.Nonnull; import javax.annotation.Nullable; -/** - * Custom {@link View} implementation that draws an RNSVGSvg React view and its children. - */ +/** Custom {@link View} implementation that draws an RNSVGSvg React view and its children. */ @SuppressLint("ViewConstructor") -public class SvgView extends FabricEnabledViewGroup implements ReactCompoundView, ReactCompoundViewGroup { +public class SvgView extends FabricEnabledViewGroup + implements ReactCompoundView, ReactCompoundViewGroup { - @Override - public boolean interceptsTouchEvent(float touchX, float touchY) { - return true; + @Override + public boolean interceptsTouchEvent(float touchX, float touchY) { + return true; + } + + @SuppressWarnings("unused") + public enum Events { + EVENT_DATA_URL("onDataURL"); + + private final String mName; + + Events(final String name) { + mName = name; } - @SuppressWarnings("unused") - public enum Events { - EVENT_DATA_URL("onDataURL"); - - private final String mName; - - Events(final String name) { - mName = name; - } - - @Nonnull - public String toString() { - return mName; - } + @Nonnull + public String toString() { + return mName; } + } - private @Nullable Bitmap mBitmap; + private @Nullable Bitmap mBitmap; - public SvgView(ReactContext reactContext) { - super(reactContext); - mScale = DisplayMetricsHolder.getScreenDisplayMetrics().density; - // for some reason on Fabric the `onDraw` won't be called without it - setWillNotDraw(false); - } + public SvgView(ReactContext reactContext) { + super(reactContext); + mScale = DisplayMetricsHolder.getScreenDisplayMetrics().density; + // for some reason on Fabric the `onDraw` won't be called without it + setWillNotDraw(false); + } - @Override - public void setId(int id) { - super.setId(id); - SvgViewManager.setSvgView(id, this); - } + @Override + public void setId(int id) { + super.setId(id); + SvgViewManager.setSvgView(id, this); + } - @Override - public void invalidate() { - super.invalidate(); - ViewParent parent = getParent(); - if (parent instanceof VirtualView) { - if (!mRendered) { - return; - } - mRendered = false; - ((VirtualView) parent).getSvgView().invalidate(); - return; - } - if (mBitmap != null) { - mBitmap.recycle(); - } - mBitmap = null; - } - - @Override - protected void onDraw(Canvas canvas) { - if (getParent() instanceof VirtualView) { - return; - } - super.onDraw(canvas); - if (mBitmap == null) { - mBitmap = drawOutput(); - } - if (mBitmap != null) { - canvas.drawBitmap(mBitmap, 0, 0, null); - if (toDataUrlTask != null) { - toDataUrlTask.run(); - toDataUrlTask = null; - } - } - } - - private Runnable toDataUrlTask = null; - - void setToDataUrlTask(Runnable task) { - toDataUrlTask = task; - } - - @Override - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - super.onSizeChanged(w, h, oldw, oldh); - this.invalidate(); - } - - @Override - public int reactTagForTouch(float touchX, float touchY) { - return hitTest(touchX, touchY); - } - - private boolean mResponsible = false; - - private final Map mDefinedClipPaths = new HashMap<>(); - private final Map mDefinedTemplates = new HashMap<>(); - private final Map mDefinedMarkers = new HashMap<>(); - private final Map mDefinedMasks = new HashMap<>(); - private final Map mDefinedBrushes = new HashMap<>(); - private Canvas mCanvas; - private final float mScale; - - private float mMinX; - private float mMinY; - private float mVbWidth; - private float mVbHeight; - private SVGLength mbbWidth; - private SVGLength mbbHeight; - private String mAlign; - private int mMeetOrSlice; - final Matrix mInvViewBoxMatrix = new Matrix(); - private boolean mInvertible = true; - private boolean mRendered = false; - int mTintColor = 0; - - boolean notRendered() { - return !mRendered; - } - - private void clearChildCache() { - if (!mRendered) { - return; - } - mRendered = false; - for (int i = 0; i < getChildCount(); i++) { - View node = getChildAt(i); - if (node instanceof VirtualView) { - VirtualView n = ((VirtualView)node); - n.clearChildCache(); - } - } - } - - @ReactProp(name = "tintColor") - public void setTintColor(@Nullable Dynamic tintColor) { - switch (tintColor.getType()) { - case Map: - mTintColor = ColorPropConverter.getColor(tintColor.asMap(), getContext()); - break; - case Number: - mTintColor = tintColor.asInt(); - break; - default: - mTintColor = 0; + @Override + public void invalidate() { + super.invalidate(); + ViewParent parent = getParent(); + if (parent instanceof VirtualView) { + if (!mRendered) { + return; } - invalidate(); - clearChildCache(); + mRendered = false; + ((VirtualView) parent).getSvgView().invalidate(); + return; } + if (mBitmap != null) { + mBitmap.recycle(); + } + mBitmap = null; + } - public void setTintColor(@Nullable Integer tintColor) { - mTintColor = tintColor; + @Override + protected void onDraw(Canvas canvas) { + if (getParent() instanceof VirtualView) { + return; + } + super.onDraw(canvas); + if (mBitmap == null) { + mBitmap = drawOutput(); + } + if (mBitmap != null) { + canvas.drawBitmap(mBitmap, 0, 0, null); + if (toDataUrlTask != null) { + toDataUrlTask.run(); + toDataUrlTask = null; + } + } + } + + private Runnable toDataUrlTask = null; + + void setToDataUrlTask(Runnable task) { + toDataUrlTask = task; + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + this.invalidate(); + } + + @Override + public int reactTagForTouch(float touchX, float touchY) { + return hitTest(touchX, touchY); + } + + private boolean mResponsible = false; + + private final Map mDefinedClipPaths = new HashMap<>(); + private final Map mDefinedTemplates = new HashMap<>(); + private final Map mDefinedMarkers = new HashMap<>(); + private final Map mDefinedMasks = new HashMap<>(); + private final Map mDefinedBrushes = new HashMap<>(); + private Canvas mCanvas; + private final float mScale; + + private float mMinX; + private float mMinY; + private float mVbWidth; + private float mVbHeight; + private SVGLength mbbWidth; + private SVGLength mbbHeight; + private String mAlign; + private int mMeetOrSlice; + final Matrix mInvViewBoxMatrix = new Matrix(); + private boolean mInvertible = true; + private boolean mRendered = false; + int mTintColor = 0; + + boolean notRendered() { + return !mRendered; + } + + private void clearChildCache() { + if (!mRendered) { + return; + } + mRendered = false; + for (int i = 0; i < getChildCount(); i++) { + View node = getChildAt(i); + if (node instanceof VirtualView) { + VirtualView n = ((VirtualView) node); + n.clearChildCache(); + } + } + } + + @ReactProp(name = "tintColor") + public void setTintColor(@Nullable Dynamic tintColor) { + switch (tintColor.getType()) { + case Map: + mTintColor = ColorPropConverter.getColor(tintColor.asMap(), getContext()); + break; + case Number: + mTintColor = tintColor.asInt(); + break; + default: + mTintColor = 0; + } invalidate(); clearChildCache(); } - @ReactProp(name = "minX") - public void setMinX(float minX) { - mMinX = minX; - invalidate(); - clearChildCache(); - } + public void setTintColor(@Nullable Integer tintColor) { + mTintColor = tintColor; + invalidate(); + clearChildCache(); + } - @ReactProp(name = "minY") - public void setMinY(float minY) { - mMinY = minY; - invalidate(); - clearChildCache(); - } + @ReactProp(name = "minX") + public void setMinX(float minX) { + mMinX = minX; + invalidate(); + clearChildCache(); + } - @ReactProp(name = "vbWidth") - public void setVbWidth(float vbWidth) { - mVbWidth = vbWidth; - invalidate(); - clearChildCache(); - } + @ReactProp(name = "minY") + public void setMinY(float minY) { + mMinY = minY; + invalidate(); + clearChildCache(); + } - @ReactProp(name = "vbHeight") - public void setVbHeight(float vbHeight) { - mVbHeight = vbHeight; - invalidate(); - clearChildCache(); - } + @ReactProp(name = "vbWidth") + public void setVbWidth(float vbWidth) { + mVbWidth = vbWidth; + invalidate(); + clearChildCache(); + } - @ReactProp(name = "bbWidth") - public void setBbWidth(Dynamic bbWidth) { - mbbWidth = SVGLength.from(bbWidth); - invalidate(); - clearChildCache(); - } + @ReactProp(name = "vbHeight") + public void setVbHeight(float vbHeight) { + mVbHeight = vbHeight; + invalidate(); + clearChildCache(); + } + + @ReactProp(name = "bbWidth") + public void setBbWidth(Dynamic bbWidth) { + mbbWidth = SVGLength.from(bbWidth); + invalidate(); + clearChildCache(); + } public void setBbWidth(String bbWidth) { mbbWidth = SVGLength.from(bbWidth); @@ -238,12 +230,12 @@ public class SvgView extends FabricEnabledViewGroup implements ReactCompoundView clearChildCache(); } - @ReactProp(name = "bbHeight") - public void setBbHeight(Dynamic bbHeight) { - mbbHeight = SVGLength.from(bbHeight); - invalidate(); - clearChildCache(); - } + @ReactProp(name = "bbHeight") + public void setBbHeight(Dynamic bbHeight) { + mbbHeight = SVGLength.from(bbHeight); + invalidate(); + clearChildCache(); + } public void setBbHeight(String bbHeight) { mbbHeight = SVGLength.from(bbHeight); @@ -251,202 +243,198 @@ public class SvgView extends FabricEnabledViewGroup implements ReactCompoundView clearChildCache(); } - @ReactProp(name = "align") - public void setAlign(String align) { - mAlign = align; - invalidate(); - clearChildCache(); + @ReactProp(name = "align") + public void setAlign(String align) { + mAlign = align; + invalidate(); + clearChildCache(); + } + + @ReactProp(name = "meetOrSlice") + public void setMeetOrSlice(int meetOrSlice) { + mMeetOrSlice = meetOrSlice; + invalidate(); + clearChildCache(); + } + + private Bitmap drawOutput() { + mRendered = true; + float width = getWidth(); + float height = getHeight(); + boolean invalid = + Float.isNaN(width) + || Float.isNaN(height) + || width < 1 + || height < 1 + || (Math.log10(width) + Math.log10(height) > 42); + if (invalid) { + return null; + } + Bitmap bitmap = Bitmap.createBitmap((int) width, (int) height, Bitmap.Config.ARGB_8888); + + drawChildren(new Canvas(bitmap)); + return bitmap; + } + + Rect getCanvasBounds() { + return mCanvas.getClipBounds(); + } + + synchronized void drawChildren(final Canvas canvas) { + mRendered = true; + mCanvas = canvas; + Matrix mViewBoxMatrix = new Matrix(); + if (mAlign != null) { + RectF vbRect = getViewBox(); + float width = canvas.getWidth(); + float height = canvas.getHeight(); + boolean nested = getParent() instanceof VirtualView; + if (nested) { + width = (float) PropHelper.fromRelative(mbbWidth, width, 0f, mScale, 12); + height = (float) PropHelper.fromRelative(mbbHeight, height, 0f, mScale, 12); + } + RectF eRect = new RectF(0, 0, width, height); + if (nested) { + canvas.clipRect(eRect); + } + mViewBoxMatrix = ViewBox.getTransform(vbRect, eRect, mAlign, mMeetOrSlice); + mInvertible = mViewBoxMatrix.invert(mInvViewBoxMatrix); + canvas.concat(mViewBoxMatrix); } - @ReactProp(name = "meetOrSlice") - public void setMeetOrSlice(int meetOrSlice) { - mMeetOrSlice = meetOrSlice; - invalidate(); - clearChildCache(); + final Paint paint = new Paint(); + + paint.setFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG | Paint.SUBPIXEL_TEXT_FLAG); + + paint.setTypeface(Typeface.DEFAULT); + + for (int i = 0; i < getChildCount(); i++) { + View node = getChildAt(i); + if (node instanceof VirtualView) { + ((VirtualView) node).saveDefinition(); + } } - private Bitmap drawOutput() { - mRendered = true; - float width = getWidth(); - float height = getHeight(); - boolean invalid = Float.isNaN(width) || Float.isNaN(height) || width < 1 || height < 1 || (Math.log10(width) + Math.log10(height) > 42); - if (invalid) { - return null; + for (int i = 0; i < getChildCount(); i++) { + View lNode = getChildAt(i); + if (lNode instanceof VirtualView) { + VirtualView node = (VirtualView) lNode; + int count = node.saveAndSetupCanvas(canvas, mViewBoxMatrix); + node.render(canvas, paint, 1f); + node.restoreCanvas(canvas, count); + + if (node.isResponsible() && !mResponsible) { + mResponsible = true; } - Bitmap bitmap = Bitmap.createBitmap( - (int) width, - (int) height, - Bitmap.Config.ARGB_8888); + } + } + } - drawChildren(new Canvas(bitmap)); - return bitmap; + private RectF getViewBox() { + return new RectF( + mMinX * mScale, mMinY * mScale, (mMinX + mVbWidth) * mScale, (mMinY + mVbHeight) * mScale); + } + + String toDataURL() { + Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); + + clearChildCache(); + drawChildren(new Canvas(bitmap)); + clearChildCache(); + this.invalidate(); + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream); + bitmap.recycle(); + byte[] bitmapBytes = stream.toByteArray(); + return Base64.encodeToString(bitmapBytes, Base64.DEFAULT); + } + + String toDataURL(int width, int height) { + Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + + clearChildCache(); + drawChildren(new Canvas(bitmap)); + clearChildCache(); + this.invalidate(); + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream); + bitmap.recycle(); + byte[] bitmapBytes = stream.toByteArray(); + return Base64.encodeToString(bitmapBytes, Base64.DEFAULT); + } + + void enableTouchEvents() { + if (!mResponsible) { + mResponsible = true; + } + } + + boolean isResponsible() { + return mResponsible; + } + + private int hitTest(float touchX, float touchY) { + if (!mResponsible || !mInvertible) { + return getId(); } - Rect getCanvasBounds() { - return mCanvas.getClipBounds(); + float[] transformed = {touchX, touchY}; + mInvViewBoxMatrix.mapPoints(transformed); + + int count = getChildCount(); + int viewTag = -1; + for (int i = count - 1; i >= 0; i--) { + View child = getChildAt(i); + if (child instanceof VirtualView) { + viewTag = ((VirtualView) child).hitTest(transformed); + } else if (child instanceof SvgView) { + viewTag = ((SvgView) child).hitTest(touchX, touchY); + } + if (viewTag != -1) { + break; + } } - synchronized void drawChildren(final Canvas canvas) { - mRendered = true; - mCanvas = canvas; - Matrix mViewBoxMatrix = new Matrix(); - if (mAlign != null) { - RectF vbRect = getViewBox(); - float width = canvas.getWidth(); - float height = canvas.getHeight(); - boolean nested = getParent() instanceof VirtualView; - if (nested) { - width = (float) PropHelper.fromRelative(mbbWidth, width, 0f, mScale, 12); - height = (float) PropHelper.fromRelative(mbbHeight, height, 0f, mScale, 12); - } - RectF eRect = new RectF(0,0, width, height); - if (nested) { - canvas.clipRect(eRect); - } - mViewBoxMatrix = ViewBox.getTransform(vbRect, eRect, mAlign, mMeetOrSlice); - mInvertible = mViewBoxMatrix.invert(mInvViewBoxMatrix); - canvas.concat(mViewBoxMatrix); - } + return viewTag == -1 ? getId() : viewTag; + } - final Paint paint = new Paint(); + void defineClipPath(VirtualView clipPath, String clipPathRef) { + mDefinedClipPaths.put(clipPathRef, clipPath); + } - paint.setFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG | Paint.SUBPIXEL_TEXT_FLAG); + VirtualView getDefinedClipPath(String clipPathRef) { + return mDefinedClipPaths.get(clipPathRef); + } - paint.setTypeface(Typeface.DEFAULT); + void defineTemplate(VirtualView template, String templateRef) { + mDefinedTemplates.put(templateRef, template); + } + VirtualView getDefinedTemplate(String templateRef) { + return mDefinedTemplates.get(templateRef); + } - for (int i = 0; i < getChildCount(); i++) { - View node = getChildAt(i); - if (node instanceof VirtualView) { - ((VirtualView)node).saveDefinition(); - } - } + void defineBrush(Brush brush, String brushRef) { + mDefinedBrushes.put(brushRef, brush); + } - for (int i = 0; i < getChildCount(); i++) { - View lNode = getChildAt(i); - if (lNode instanceof VirtualView) { - VirtualView node = (VirtualView)lNode; - int count = node.saveAndSetupCanvas(canvas, mViewBoxMatrix); - node.render(canvas, paint, 1f); - node.restoreCanvas(canvas, count); + Brush getDefinedBrush(String brushRef) { + return mDefinedBrushes.get(brushRef); + } - if (node.isResponsible() && !mResponsible) { - mResponsible = true; - } - } - } - } + void defineMask(VirtualView mask, String maskRef) { + mDefinedMasks.put(maskRef, mask); + } - private RectF getViewBox() { - return new RectF(mMinX * mScale, mMinY * mScale, (mMinX + mVbWidth) * mScale, (mMinY + mVbHeight) * mScale); - } + VirtualView getDefinedMask(String maskRef) { + return mDefinedMasks.get(maskRef); + } - String toDataURL() { - Bitmap bitmap = Bitmap.createBitmap( - getWidth(), - getHeight(), - Bitmap.Config.ARGB_8888); + void defineMarker(VirtualView marker, String markerRef) { + mDefinedMarkers.put(markerRef, marker); + } - clearChildCache(); - drawChildren(new Canvas(bitmap)); - clearChildCache(); - this.invalidate(); - ByteArrayOutputStream stream = new ByteArrayOutputStream(); - bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream); - bitmap.recycle(); - byte[] bitmapBytes = stream.toByteArray(); - return Base64.encodeToString(bitmapBytes, Base64.DEFAULT); - } - - String toDataURL(int width, int height) { - Bitmap bitmap = Bitmap.createBitmap( - width, - height, - Bitmap.Config.ARGB_8888); - - clearChildCache(); - drawChildren(new Canvas(bitmap)); - clearChildCache(); - this.invalidate(); - ByteArrayOutputStream stream = new ByteArrayOutputStream(); - bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream); - bitmap.recycle(); - byte[] bitmapBytes = stream.toByteArray(); - return Base64.encodeToString(bitmapBytes, Base64.DEFAULT); - } - - void enableTouchEvents() { - if (!mResponsible) { - mResponsible = true; - } - } - - boolean isResponsible() { - return mResponsible; - } - - private int hitTest(float touchX, float touchY) { - if (!mResponsible || !mInvertible) { - return getId(); - } - - float[] transformed = { touchX, touchY }; - mInvViewBoxMatrix.mapPoints(transformed); - - int count = getChildCount(); - int viewTag = -1; - for (int i = count - 1; i >= 0; i--) { - View child = getChildAt(i); - if (child instanceof VirtualView) { - viewTag = ((VirtualView) child).hitTest(transformed); - } else if (child instanceof SvgView) { - viewTag = ((SvgView) child).hitTest(touchX, touchY); - } - if (viewTag != -1) { - break; - } - } - - return viewTag == -1 ? getId() : viewTag; - } - - void defineClipPath(VirtualView clipPath, String clipPathRef) { - mDefinedClipPaths.put(clipPathRef, clipPath); - } - - VirtualView getDefinedClipPath(String clipPathRef) { - return mDefinedClipPaths.get(clipPathRef); - } - - void defineTemplate(VirtualView template, String templateRef) { - mDefinedTemplates.put(templateRef, template); - } - - VirtualView getDefinedTemplate(String templateRef) { - return mDefinedTemplates.get(templateRef); - } - - void defineBrush(Brush brush, String brushRef) { - mDefinedBrushes.put(brushRef, brush); - } - - Brush getDefinedBrush(String brushRef) { - return mDefinedBrushes.get(brushRef); - } - - void defineMask(VirtualView mask, String maskRef) { - mDefinedMasks.put(maskRef, mask); - } - - VirtualView getDefinedMask(String maskRef) { - return mDefinedMasks.get(maskRef); - } - - void defineMarker(VirtualView marker, String markerRef) { - mDefinedMarkers.put(markerRef, marker); - } - - VirtualView getDefinedMarker(String markerRef) { - return mDefinedMarkers.get(markerRef); - } + VirtualView getDefinedMarker(String markerRef) { + return mDefinedMarkers.get(markerRef); + } } diff --git a/android/src/main/java/com/horcrux/svg/SvgViewManager.java b/android/src/main/java/com/horcrux/svg/SvgViewManager.java index 77eadb28..a337788e 100644 --- a/android/src/main/java/com/horcrux/svg/SvgViewManager.java +++ b/android/src/main/java/com/horcrux/svg/SvgViewManager.java @@ -6,145 +6,142 @@ * LICENSE file in the root directory of this source tree. */ - package com.horcrux.svg; import android.util.SparseArray; - import com.facebook.react.bridge.Dynamic; import com.facebook.react.uimanager.ThemedReactContext; import com.facebook.react.uimanager.ViewManagerDelegate; import com.facebook.react.uimanager.annotations.ReactProp; -import com.facebook.react.views.view.ReactViewGroup; -import com.facebook.react.views.view.ReactViewManager; import com.facebook.react.viewmanagers.RNSVGSvgViewManagerDelegate; import com.facebook.react.viewmanagers.RNSVGSvgViewManagerInterface; - +import com.facebook.react.views.view.ReactViewGroup; +import com.facebook.react.views.view.ReactViewManager; import javax.annotation.Nonnull; import javax.annotation.Nullable; /** - * ViewManager for RNSVGSvgView React views. Renders as a {@link SvgView} and handles - * invalidating the native view on view updates happening in the underlying tree. + * ViewManager for RNSVGSvgView React views. Renders as a {@link SvgView} and handles invalidating + * the native view on view updates happening in the underlying tree. */ class SvgViewManager extends ReactViewManager implements RNSVGSvgViewManagerInterface { - private static final String REACT_CLASS = "RNSVGSvgView"; + private static final String REACT_CLASS = "RNSVGSvgView"; - private static final SparseArray mTagToSvgView = new SparseArray<>(); - private static final SparseArray mTagToRunnable = new SparseArray<>(); + private static final SparseArray mTagToSvgView = new SparseArray<>(); + private static final SparseArray mTagToRunnable = new SparseArray<>(); - private final ViewManagerDelegate mDelegate; + private final ViewManagerDelegate mDelegate; - protected ViewManagerDelegate getDelegate(){ - return mDelegate; + protected ViewManagerDelegate getDelegate() { + return mDelegate; + } + + public SvgViewManager() { + mDelegate = new RNSVGSvgViewManagerDelegate(this); + } + + static void setSvgView(int tag, SvgView svg) { + mTagToSvgView.put(tag, svg); + Runnable task = mTagToRunnable.get(tag); + if (task != null) { + task.run(); + mTagToRunnable.delete(tag); } + } - public SvgViewManager() { - mDelegate = new RNSVGSvgViewManagerDelegate(this); - } + static void runWhenViewIsAvailable(int tag, Runnable task) { + mTagToRunnable.put(tag, task); + } - static void setSvgView(int tag, SvgView svg) { - mTagToSvgView.put(tag, svg); - Runnable task = mTagToRunnable.get(tag); - if (task != null) { - task.run(); - mTagToRunnable.delete(tag); - } - } + static @Nullable SvgView getSvgViewByTag(int tag) { + return mTagToSvgView.get(tag); + } - static void runWhenViewIsAvailable(int tag, Runnable task) { - mTagToRunnable.put(tag, task); - } + @Nonnull + @Override + public String getName() { + return REACT_CLASS; + } - static @Nullable SvgView getSvgViewByTag(int tag) { - return mTagToSvgView.get(tag); - } + @Nonnull + @Override + public ReactViewGroup createViewInstance(ThemedReactContext reactContext) { + return new SvgView(reactContext); + } - @Nonnull - @Override - public String getName() { - return REACT_CLASS; - } + @Override + public void updateExtraData(ReactViewGroup root, Object extraData) { + super.updateExtraData(root, extraData); + root.invalidate(); + } - @Nonnull - @Override - public ReactViewGroup createViewInstance(ThemedReactContext reactContext) { - return new SvgView(reactContext); - } + @Override + public void onDropViewInstance(@Nonnull ReactViewGroup view) { + super.onDropViewInstance(view); + mTagToSvgView.remove(view.getId()); + } - @Override - public void updateExtraData(ReactViewGroup root, Object extraData) { - super.updateExtraData(root, extraData); - root.invalidate(); - } + @Override + public boolean needsCustomLayoutForChildren() { + return true; + } - @Override - public void onDropViewInstance(@Nonnull ReactViewGroup view) { - super.onDropViewInstance(view); - mTagToSvgView.remove(view.getId()); - } + @ReactProp(name = "tintColor") + public void setTintColor(SvgView node, @Nullable Dynamic tintColor) { + node.setTintColor(tintColor); + } - @Override - public boolean needsCustomLayoutForChildren() { - return true; - } + @ReactProp(name = "color") + public void setColor(SvgView node, @Nullable Dynamic color) { + node.setTintColor(color); + } - @ReactProp(name = "tintColor") - public void setTintColor(SvgView node, @Nullable Dynamic tintColor) { - node.setTintColor(tintColor); - } + @ReactProp(name = "minX") + @Override + public void setMinX(SvgView node, float minX) { + node.setMinX(minX); + } - @ReactProp(name = "color") - public void setColor(SvgView node, @Nullable Dynamic color) { - node.setTintColor(color); - } + @ReactProp(name = "minY") + @Override + public void setMinY(SvgView node, float minY) { + node.setMinY(minY); + } - @ReactProp(name = "minX") - @Override - public void setMinX(SvgView node, float minX) { - node.setMinX(minX); - } + @ReactProp(name = "vbWidth") + @Override + public void setVbWidth(SvgView node, float vbWidth) { + node.setVbWidth(vbWidth); + } - @ReactProp(name = "minY") - @Override - public void setMinY(SvgView node, float minY) { - node.setMinY(minY); - } + @ReactProp(name = "vbHeight") + @Override + public void setVbHeight(SvgView node, float vbHeight) { + node.setVbHeight(vbHeight); + } - @ReactProp(name = "vbWidth") - @Override - public void setVbWidth(SvgView node, float vbWidth) { - node.setVbWidth(vbWidth); - } + @ReactProp(name = "bbWidth") + public void setBbWidth(SvgView node, Dynamic bbWidth) { + node.setBbWidth(bbWidth); + } - @ReactProp(name = "vbHeight") - @Override - public void setVbHeight(SvgView node, float vbHeight) { - node.setVbHeight(vbHeight); - } + @ReactProp(name = "bbHeight") + public void setBbHeight(SvgView node, Dynamic bbHeight) { + node.setBbHeight(bbHeight); + } - @ReactProp(name = "bbWidth") - public void setBbWidth(SvgView node, Dynamic bbWidth) { - node.setBbWidth(bbWidth); - } + @ReactProp(name = "align") + @Override + public void setAlign(SvgView node, String align) { + node.setAlign(align); + } - @ReactProp(name = "bbHeight") - public void setBbHeight(SvgView node, Dynamic bbHeight) { - node.setBbHeight(bbHeight); - } - - @ReactProp(name = "align") - @Override - public void setAlign(SvgView node, String align) { - node.setAlign(align); - } - - @ReactProp(name = "meetOrSlice") - @Override - public void setMeetOrSlice(SvgView node, int meetOrSlice) { - node.setMeetOrSlice(meetOrSlice); - } + @ReactProp(name = "meetOrSlice") + @Override + public void setMeetOrSlice(SvgView node, int meetOrSlice) { + node.setMeetOrSlice(meetOrSlice); + } @Override public void setTintColor(SvgView view, @Nullable Integer value) { @@ -165,5 +162,4 @@ class SvgViewManager extends ReactViewManager implements RNSVGSvgViewManagerInte public void setBbHeight(SvgView view, @Nullable String value) { view.setBbHeight(value); } - } diff --git a/android/src/main/java/com/horcrux/svg/SvgViewModule.java b/android/src/main/java/com/horcrux/svg/SvgViewModule.java index f1e3b66b..30397a6e 100644 --- a/android/src/main/java/com/horcrux/svg/SvgViewModule.java +++ b/android/src/main/java/com/horcrux/svg/SvgViewModule.java @@ -6,7 +6,6 @@ * LICENSE file in the root directory of this source tree. */ - package com.horcrux.svg; import com.facebook.react.bridge.Callback; @@ -15,70 +14,69 @@ import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.UiThreadUtil; - import javax.annotation.Nonnull; class SvgViewModule extends ReactContextBaseJavaModule { - SvgViewModule(ReactApplicationContext reactContext) { - super(reactContext); - } + SvgViewModule(ReactApplicationContext reactContext) { + super(reactContext); + } - @Nonnull - @Override - public String getName() { - return "RNSVGSvgViewManager"; - } + @Nonnull + @Override + public String getName() { + return "RNSVGSvgViewManager"; + } - private static void toDataURL(final int tag, final ReadableMap options, final Callback successCallback, final int attempt) { - UiThreadUtil.runOnUiThread( - new Runnable() { + private static void toDataURL( + final int tag, final ReadableMap options, final Callback successCallback, final int attempt) { + UiThreadUtil.runOnUiThread( + new Runnable() { + @Override + public void run() { + SvgView svg = SvgViewManager.getSvgViewByTag(tag); + + if (svg == null) { + SvgViewManager.runWhenViewIsAvailable( + tag, + new Runnable() { @Override public void run() { - SvgView svg = SvgViewManager.getSvgViewByTag(tag); - - if (svg == null) { - SvgViewManager.runWhenViewIsAvailable(tag, new Runnable() { - @Override - public void run() { - SvgView svg = SvgViewManager.getSvgViewByTag(tag); - if (svg == null) { // Should never happen - return; - } - svg.setToDataUrlTask(new Runnable() { - @Override - public void run() { - toDataURL(tag, options, successCallback, attempt + 1); - } - }); - } - }); - } else if (svg.notRendered()) { - svg.setToDataUrlTask(new Runnable() { - @Override - public void run() { - toDataURL(tag, options, successCallback, attempt + 1); - } - }); - } else { - if (options != null) { - successCallback.invoke( - svg.toDataURL( - options.getInt("width"), - options.getInt("height") - ) - ); - } else { - successCallback.invoke(svg.toDataURL()); + SvgView svg = SvgViewManager.getSvgViewByTag(tag); + if (svg == null) { // Should never happen + return; + } + svg.setToDataUrlTask( + new Runnable() { + @Override + public void run() { + toDataURL(tag, options, successCallback, attempt + 1); } - } + }); } - } - ); - } + }); + } else if (svg.notRendered()) { + svg.setToDataUrlTask( + new Runnable() { + @Override + public void run() { + toDataURL(tag, options, successCallback, attempt + 1); + } + }); + } else { + if (options != null) { + successCallback.invoke( + svg.toDataURL(options.getInt("width"), options.getInt("height"))); + } else { + successCallback.invoke(svg.toDataURL()); + } + } + } + }); + } - @SuppressWarnings("unused") - @ReactMethod - public void toDataURL(int tag, ReadableMap options, Callback successCallback) { - toDataURL(tag, options, successCallback, 0); - } + @SuppressWarnings("unused") + @ReactMethod + public void toDataURL(int tag, ReadableMap options, Callback successCallback) { + toDataURL(tag, options, successCallback, 0); + } } diff --git a/android/src/main/java/com/horcrux/svg/SymbolView.java b/android/src/main/java/com/horcrux/svg/SymbolView.java index b076fe04..d091568a 100644 --- a/android/src/main/java/com/horcrux/svg/SymbolView.java +++ b/android/src/main/java/com/horcrux/svg/SymbolView.java @@ -6,7 +6,6 @@ * LICENSE file in the root directory of this source tree. */ - package com.horcrux.svg; import android.annotation.SuppressLint; @@ -14,72 +13,76 @@ import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.RectF; - import com.facebook.react.bridge.ReactContext; import com.facebook.react.uimanager.annotations.ReactProp; @SuppressLint("ViewConstructor") class SymbolView extends GroupView { - private float mMinX; - private float mMinY; - private float mVbWidth; - private float mVbHeight; - private String mAlign; - private int mMeetOrSlice; + private float mMinX; + private float mMinY; + private float mVbWidth; + private float mVbHeight; + private String mAlign; + private int mMeetOrSlice; - public SymbolView(ReactContext reactContext) { - super(reactContext); - } + public SymbolView(ReactContext reactContext) { + super(reactContext); + } - @ReactProp(name = "minX") - public void setMinX(float minX) { - mMinX = minX; - invalidate(); - } + @ReactProp(name = "minX") + public void setMinX(float minX) { + mMinX = minX; + invalidate(); + } - @ReactProp(name = "minY") - public void setMinY(float minY) { - mMinY = minY; - invalidate(); - } + @ReactProp(name = "minY") + public void setMinY(float minY) { + mMinY = minY; + invalidate(); + } - @ReactProp(name = "vbWidth") - public void setVbWidth(float vbWidth) { - mVbWidth = vbWidth; - invalidate(); - } + @ReactProp(name = "vbWidth") + public void setVbWidth(float vbWidth) { + mVbWidth = vbWidth; + invalidate(); + } - @ReactProp(name = "vbHeight") - public void setVbHeight(float vbHeight) { - mVbHeight = vbHeight; - invalidate(); - } + @ReactProp(name = "vbHeight") + public void setVbHeight(float vbHeight) { + mVbHeight = vbHeight; + invalidate(); + } - @ReactProp(name = "align") - public void setAlign(String align) { - mAlign = align; - invalidate(); - } + @ReactProp(name = "align") + public void setAlign(String align) { + mAlign = align; + invalidate(); + } - @ReactProp(name = "meetOrSlice") - public void setMeetOrSlice(int meetOrSlice) { - mMeetOrSlice = meetOrSlice; - invalidate(); - } + @ReactProp(name = "meetOrSlice") + public void setMeetOrSlice(int meetOrSlice) { + mMeetOrSlice = meetOrSlice; + invalidate(); + } - @Override - void draw(Canvas canvas, Paint paint, float opacity) { - saveDefinition(); - } + @Override + void draw(Canvas canvas, Paint paint, float opacity) { + saveDefinition(); + } - void drawSymbol(Canvas canvas, Paint paint, float opacity, float width, float height) { - if (mAlign != null) { - RectF vbRect = new RectF(mMinX * mScale, mMinY * mScale, (mMinX + mVbWidth) * mScale, (mMinY + mVbHeight) * mScale); - RectF eRect = new RectF(0, 0, width, height); - Matrix viewBoxMatrix = ViewBox.getTransform(vbRect, eRect, mAlign, mMeetOrSlice); - canvas.concat(viewBoxMatrix); - super.draw(canvas, paint, opacity); - } + void drawSymbol(Canvas canvas, Paint paint, float opacity, float width, float height) { + if (mAlign != null) { + RectF vbRect = + new RectF( + mMinX * mScale, + mMinY * mScale, + (mMinX + mVbWidth) * mScale, + (mMinY + mVbHeight) * mScale); + RectF eRect = new RectF(0, 0, width, height); + Matrix viewBoxMatrix = ViewBox.getTransform(vbRect, eRect, mAlign, mMeetOrSlice); + canvas.concat(viewBoxMatrix); + super.draw(canvas, paint, opacity); } + } } diff --git a/android/src/main/java/com/horcrux/svg/TSpanView.java b/android/src/main/java/com/horcrux/svg/TSpanView.java index da9f4699..3c57486c 100644 --- a/android/src/main/java/com/horcrux/svg/TSpanView.java +++ b/android/src/main/java/com/horcrux/svg/TSpanView.java @@ -6,9 +6,19 @@ * LICENSE file in the root directory of this source tree. */ - package com.horcrux.svg; +import static android.graphics.Matrix.MTRANS_X; +import static android.graphics.Matrix.MTRANS_Y; +import static android.graphics.PathMeasure.POSITION_MATRIX_FLAG; +import static android.graphics.PathMeasure.TANGENT_MATRIX_FLAG; +import static com.horcrux.svg.TextProperties.AlignmentBaseline; +import static com.horcrux.svg.TextProperties.FontStyle; +import static com.horcrux.svg.TextProperties.FontVariantLigatures; +import static com.horcrux.svg.TextProperties.FontWeight; +import static com.horcrux.svg.TextProperties.TextAnchor; +import static com.horcrux.svg.TextProperties.TextPathMidLine; +import static com.horcrux.svg.TextProperties.TextPathSide; import android.annotation.SuppressLint; import android.content.res.AssetManager; @@ -27,1225 +37,1207 @@ import android.text.StaticLayout; import android.text.TextPaint; import android.view.View; import android.view.ViewParent; - import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.uimanager.annotations.ReactProp; import com.facebook.react.views.text.ReactFontManager; - -import java.util.ArrayList; - import java.text.Bidi; - +import java.util.ArrayList; import javax.annotation.Nullable; -import static android.graphics.Matrix.MTRANS_X; -import static android.graphics.Matrix.MTRANS_Y; -import static android.graphics.PathMeasure.POSITION_MATRIX_FLAG; -import static android.graphics.PathMeasure.TANGENT_MATRIX_FLAG; -import static com.horcrux.svg.TextProperties.AlignmentBaseline; -import static com.horcrux.svg.TextProperties.FontStyle; -import static com.horcrux.svg.TextProperties.FontVariantLigatures; -import static com.horcrux.svg.TextProperties.FontWeight; -import static com.horcrux.svg.TextProperties.TextAnchor; -import static com.horcrux.svg.TextProperties.TextPathMidLine; -import static com.horcrux.svg.TextProperties.TextPathSide; - @SuppressLint("ViewConstructor") class TSpanView extends TextView { - private static final double tau = 2 * Math.PI; - private static final double radToDeg = 360 / tau; + private static final double tau = 2 * Math.PI; + private static final double radToDeg = 360 / tau; - private static final String FONTS = "fonts/"; - private static final String OTF = ".otf"; - private static final String TTF = ".ttf"; + private static final String FONTS = "fonts/"; + private static final String OTF = ".otf"; + private static final String TTF = ".ttf"; - private Path mCachedPath; - @Nullable String mContent; - private TextPathView textPath; - private final ArrayList emoji = new ArrayList<>(); - private final ArrayList emojiTransforms = new ArrayList<>(); - private final AssetManager assets; + private Path mCachedPath; + @Nullable String mContent; + private TextPathView textPath; + private final ArrayList emoji = new ArrayList<>(); + private final ArrayList emojiTransforms = new ArrayList<>(); + private final AssetManager assets; - public TSpanView(ReactContext reactContext) { - super(reactContext); - assets = mContext.getResources().getAssets(); + public TSpanView(ReactContext reactContext) { + super(reactContext); + assets = mContext.getResources().getAssets(); + } + + @ReactProp(name = "content") + public void setContent(@Nullable String content) { + mContent = content; + invalidate(); + } + + @Override + public void invalidate() { + mCachedPath = null; + super.invalidate(); + } + + void clearCache() { + mCachedPath = null; + super.clearCache(); + } + + @Override + void draw(Canvas canvas, Paint paint, float opacity) { + if (mContent != null) { + if (mInlineSize != null && mInlineSize.value != 0) { + if (setupFillPaint(paint, opacity * fillOpacity)) { + drawWrappedText(canvas, paint); + } + if (setupStrokePaint(paint, opacity * strokeOpacity)) { + drawWrappedText(canvas, paint); + } + } else { + int numEmoji = emoji.size(); + if (numEmoji > 0) { + GlyphContext gc = getTextRootGlyphContext(); + FontData font = gc.getFont(); + applyTextPropertiesToPaint(paint, font); + for (int i = 0; i < numEmoji; i++) { + String current = emoji.get(i); + Matrix mid = emojiTransforms.get(i); + canvas.save(); + canvas.concat(mid); + canvas.drawText(current, 0, 0, paint); + canvas.restore(); + } + } + drawPath(canvas, paint, opacity); + } + } else { + clip(canvas, paint); + drawGroup(canvas, paint, opacity); + } + } + + private void drawWrappedText(Canvas canvas, Paint paint) { + GlyphContext gc = getTextRootGlyphContext(); + pushGlyphContext(); + FontData font = gc.getFont(); + TextPaint tp = new TextPaint(paint); + applyTextPropertiesToPaint(tp, font); + applySpacingAndFeatures(tp, font); + double fontSize = gc.getFontSize(); + + Layout.Alignment align; + switch (font.textAnchor) { + default: + case start: + align = Layout.Alignment.ALIGN_NORMAL; + break; + + case middle: + align = Layout.Alignment.ALIGN_CENTER; + break; + + case end: + align = Layout.Alignment.ALIGN_OPPOSITE; + break; } - @ReactProp(name = "content") - public void setContent(@Nullable String content) { - mContent = content; - invalidate(); + boolean includeFontPadding = true; + SpannableString text = new SpannableString(mContent); + final double width = + PropHelper.fromRelative(mInlineSize, canvas.getWidth(), 0, mScale, fontSize); + StaticLayout layout = getStaticLayout(tp, align, includeFontPadding, text, (int) width); + + int lineAscent = layout.getLineAscent(0); + + float dx = (float) gc.nextX(0); + float dy = (float) (gc.nextY() + lineAscent); + popGlyphContext(); + + canvas.save(); + canvas.translate(dx, dy); + layout.draw(canvas); + canvas.restore(); + } + + @SuppressWarnings("deprecation") + private StaticLayout getStaticLayout( + TextPaint tp, + Layout.Alignment align, + boolean includeFontPadding, + SpannableString text, + int width) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { + return new StaticLayout(text, tp, width, align, 1.f, 0.f, includeFontPadding); + } else { + return StaticLayout.Builder.obtain(text, 0, text.length(), tp, width) + .setAlignment(align) + .setLineSpacing(0.f, 1.f) + .setIncludePad(includeFontPadding) + .setBreakStrategy(Layout.BREAK_STRATEGY_HIGH_QUALITY) + .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) + .build(); + } + } + + /** + * Implements visual to logical order converter. + * + * @author Nesterovsky bros + * @param text an input text in visual order to convert. + * @return a String value in logical order. + */ + public static String visualToLogical(String text) { + if ((text == null) || (text.length() == 0)) { + return text; } - @Override - public void invalidate() { - mCachedPath = null; - super.invalidate(); + Bidi bidi = new Bidi(text, Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT); + + if (bidi.isLeftToRight()) { + return text; } - void clearCache() { - mCachedPath = null; - super.clearCache(); + int count = bidi.getRunCount(); + byte[] levels = new byte[count]; + Integer[] runs = new Integer[count]; + + for (int i = 0; i < count; i++) { + levels[i] = (byte) bidi.getRunLevel(i); + runs[i] = i; } - @Override - void draw(Canvas canvas, Paint paint, float opacity) { - if (mContent != null) { - if (mInlineSize != null && mInlineSize.value != 0) { - if (setupFillPaint(paint, opacity * fillOpacity)) { - drawWrappedText(canvas, paint); - } - if (setupStrokePaint(paint, opacity * strokeOpacity)) { - drawWrappedText(canvas, paint); - } - } else { - int numEmoji = emoji.size(); - if (numEmoji > 0) { - GlyphContext gc = getTextRootGlyphContext(); - FontData font = gc.getFont(); - applyTextPropertiesToPaint(paint, font); - for (int i = 0; i < numEmoji; i++) { - String current = emoji.get(i); - Matrix mid = emojiTransforms.get(i); - canvas.save(); - canvas.concat(mid); - canvas.drawText(current, 0, 0, paint); - canvas.restore(); - } - } - drawPath(canvas, paint, opacity); - } - } else { - clip(canvas, paint); - drawGroup(canvas, paint, opacity); + Bidi.reorderVisually(levels, 0, runs, 0, count); + + StringBuilder result = new StringBuilder(); + + for (int i = 0; i < count; i++) { + int index = runs[i]; + int start = bidi.getRunStart(index); + int end = bidi.getRunLimit(index); + int level = levels[index]; + + if ((level & 1) != 0) { + for (; --end >= start; ) { + result.append(text.charAt(end)); } + } else { + result.append(text, start, end); + } } - private void drawWrappedText(Canvas canvas, Paint paint) { - GlyphContext gc = getTextRootGlyphContext(); - pushGlyphContext(); - FontData font = gc.getFont(); - TextPaint tp = new TextPaint(paint); - applyTextPropertiesToPaint(tp, font); - applySpacingAndFeatures(tp, font); - double fontSize = gc.getFontSize(); + return result.toString(); + } - Layout.Alignment align; - switch (font.textAnchor) { - default: - case start: - align = Layout.Alignment.ALIGN_NORMAL; - break; - - case middle: - align = Layout.Alignment.ALIGN_CENTER; - break; - - case end: - align = Layout.Alignment.ALIGN_OPPOSITE; - break; - } - - boolean includeFontPadding = true; - SpannableString text = new SpannableString(mContent); - final double width = PropHelper.fromRelative(mInlineSize, canvas.getWidth(), 0, mScale, fontSize); - StaticLayout layout = getStaticLayout(tp, align, includeFontPadding, text, (int) width); - - int lineAscent = layout.getLineAscent(0); - - float dx = (float) gc.nextX(0); - float dy = (float) (gc.nextY() + lineAscent); - popGlyphContext(); - - canvas.save(); - canvas.translate(dx, dy); - layout.draw(canvas); - canvas.restore(); + @Override + Path getPath(Canvas canvas, Paint paint) { + if (mCachedPath != null) { + return mCachedPath; } - @SuppressWarnings("deprecation") - private StaticLayout getStaticLayout(TextPaint tp, Layout.Alignment align, boolean includeFontPadding, SpannableString text, int width) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { - return new StaticLayout( - text, - tp, - width, - align, - 1.f, - 0.f, - includeFontPadding); - } else { - return StaticLayout.Builder.obtain(text, 0, text.length(), tp, width) - .setAlignment(align) - .setLineSpacing(0.f, 1.f) - .setIncludePad(includeFontPadding) - .setBreakStrategy(Layout.BREAK_STRATEGY_HIGH_QUALITY) - .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) - .build(); - } + if (mContent == null) { + mCachedPath = getGroupPath(canvas, paint); + return mCachedPath; } + setupTextPath(); - /** - * Implements visual to logical order converter. - * - * @author Nesterovsky bros - * - * @param text an input text in visual order to convert. - * @return a String value in logical order. - */ - public static String visualToLogical(String text) - { - if ((text == null) || (text.length() == 0)) - { - return text; + pushGlyphContext(); + mCachedPath = getLinePath(visualToLogical(mContent), paint, canvas); + popGlyphContext(); + + return mCachedPath; + } + + double getSubtreeTextChunksTotalAdvance(Paint paint) { + if (!Double.isNaN(cachedAdvance)) { + return cachedAdvance; + } + double advance = 0; + + if (mContent == null) { + for (int i = 0; i < getChildCount(); i++) { + View child = getChildAt(i); + if (child instanceof TextView) { + TextView text = (TextView) child; + advance += text.getSubtreeTextChunksTotalAdvance(paint); } - - Bidi bidi = new Bidi(text, Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT); - - if (bidi.isLeftToRight()) - { - return text; - } - - int count = bidi.getRunCount(); - byte[] levels = new byte[count]; - Integer[] runs = new Integer[count]; - - for (int i = 0; i < count; i++) - { - levels[i] = (byte)bidi.getRunLevel(i); - runs[i] = i; - } - - Bidi.reorderVisually(levels, 0, runs, 0, count); - - StringBuilder result = new StringBuilder(); - - for (int i = 0; i < count; i++) - { - int index = runs[i]; - int start = bidi.getRunStart(index); - int end = bidi.getRunLimit(index); - int level = levels[index]; - - if ((level & 1) != 0) - { - for (; --end >= start;) - { - result.append(text.charAt(end)); - } - } - else - { - result.append(text, start, end); - } - } - - return result.toString(); + } + cachedAdvance = advance; + return advance; } - @Override - Path getPath(Canvas canvas, Paint paint) { - if (mCachedPath != null) { - return mCachedPath; - } + String line = mContent; + final int length = line.length(); - if (mContent == null) { - mCachedPath = getGroupPath(canvas, paint); - return mCachedPath; - } - - setupTextPath(); - - pushGlyphContext(); - mCachedPath = getLinePath(visualToLogical(mContent), paint, canvas); - popGlyphContext(); - - return mCachedPath; + if (length == 0) { + cachedAdvance = 0; + return advance; } - double getSubtreeTextChunksTotalAdvance(Paint paint) { - if (!Double.isNaN(cachedAdvance)) { - return cachedAdvance; - } - double advance = 0; + GlyphContext gc = getTextRootGlyphContext(); + FontData font = gc.getFont(); + applyTextPropertiesToPaint(paint, font); - if (mContent == null) { - for (int i = 0; i < getChildCount(); i++) { - View child = getChildAt(i); - if (child instanceof TextView) { - TextView text = (TextView)child; - advance += text.getSubtreeTextChunksTotalAdvance(paint); - } - } - cachedAdvance = advance; - return advance; - } + applySpacingAndFeatures(paint, font); - String line = mContent; - final int length = line.length(); + cachedAdvance = paint.measureText(line); + return cachedAdvance; + } - if (length == 0) { - cachedAdvance = 0; - return advance; - } + static final String requiredFontFeatures = + "'rlig', 'liga', 'clig', 'calt', 'locl', 'ccmp', 'mark', 'mkmk',"; + static final String disableDiscretionaryLigatures = + "'liga' 0, 'clig' 0, 'dlig' 0, 'hlig' 0, 'cala' 0, "; + static final String defaultFeatures = requiredFontFeatures + "'kern', "; + static final String additionalLigatures = "'hlig', 'cala', "; + static final String fontWeightTag = "'wght' "; - GlyphContext gc = getTextRootGlyphContext(); - FontData font = gc.getFont(); - applyTextPropertiesToPaint(paint, font); + private void applySpacingAndFeatures(Paint paint, FontData font) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + double letterSpacing = font.letterSpacing; + paint.setLetterSpacing((float) (letterSpacing / (font.fontSize * mScale))); - applySpacingAndFeatures(paint, font); + final boolean allowOptionalLigatures = + letterSpacing == 0 && font.fontVariantLigatures == FontVariantLigatures.normal; - cachedAdvance = paint.measureText(line); - return cachedAdvance; + if (allowOptionalLigatures) { + paint.setFontFeatureSettings( + defaultFeatures + additionalLigatures + font.fontFeatureSettings); + } else { + paint.setFontFeatureSettings( + defaultFeatures + disableDiscretionaryLigatures + font.fontFeatureSettings); + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + paint.setFontVariationSettings( + fontWeightTag + font.absoluteFontWeight + font.fontVariationSettings); + } + } + } + + @SuppressWarnings("ConstantConditions") + private Path getLinePath(String line, Paint paint, Canvas canvas) { + final int length = line.length(); + final Path path = new Path(); + + emoji.clear(); + emojiTransforms.clear(); + + if (length == 0) { + return path; } - final static String requiredFontFeatures = "'rlig', 'liga', 'clig', 'calt', 'locl', 'ccmp', 'mark', 'mkmk',"; - final static String disableDiscretionaryLigatures = "'liga' 0, 'clig' 0, 'dlig' 0, 'hlig' 0, 'cala' 0, "; - final static String defaultFeatures = requiredFontFeatures + "'kern', "; - final static String additionalLigatures = "'hlig', 'cala', "; - final static String fontWeightTag = "'wght' "; - - private void applySpacingAndFeatures(Paint paint, FontData font) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - double letterSpacing = font.letterSpacing; - paint.setLetterSpacing((float) (letterSpacing / (font.fontSize * mScale))); - - final boolean allowOptionalLigatures = letterSpacing == 0 && - font.fontVariantLigatures == FontVariantLigatures.normal; - - if (allowOptionalLigatures) { - paint.setFontFeatureSettings(defaultFeatures + additionalLigatures + font.fontFeatureSettings); - } else { - paint.setFontFeatureSettings(defaultFeatures + disableDiscretionaryLigatures + font.fontFeatureSettings); - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - paint.setFontVariationSettings(fontWeightTag + font.absoluteFontWeight + font.fontVariationSettings); - } - } - } - - @SuppressWarnings("ConstantConditions") - private Path getLinePath(String line, Paint paint, Canvas canvas) { - final int length = line.length(); - final Path path = new Path(); - - emoji.clear(); - emojiTransforms.clear(); - - if (length == 0) { - return path; - } - - double pathLength = 0; - PathMeasure pm = null; - boolean isClosed = false; - final boolean hasTextPath = textPath != null; - if (hasTextPath) { - pm = new PathMeasure(textPath.getTextPath(canvas, paint), false); - pathLength = pm.getLength(); - isClosed = pm.isClosed(); - if (pathLength == 0) { - return path; - } - } - - GlyphContext gc = getTextRootGlyphContext(); - FontData font = gc.getFont(); - applyTextPropertiesToPaint(paint, font); - GlyphPathBag bag = new GlyphPathBag(paint); - boolean[] ligature = new boolean[length]; - final char[] chars = line.toCharArray(); - - /* - * - * Three properties affect the space between characters and words: - * - * ‘kerning’ indicates whether the user agent should adjust inter-glyph spacing - * based on kerning tables that are included in the relevant font - * (i.e., enable auto-kerning) or instead disable auto-kerning - * and instead set inter-character spacing to a specific length (typically, zero). - * - * ‘letter-spacing’ indicates an amount of space that is to be added between text - * characters supplemental to any spacing due to the ‘kerning’ property. - * - * ‘word-spacing’ indicates the spacing behavior between words. - * - * Letter-spacing is applied after bidi reordering and is in addition to any word-spacing. - * Depending on the justification rules in effect, user agents may further increase - * or decrease the space between typographic character units in order to justify text. - * - * */ - double kerning = font.kerning; - double wordSpacing = font.wordSpacing; - double letterSpacing = font.letterSpacing; - final boolean autoKerning = !font.manualKerning; - - /* - 11.1.2. Fonts and glyphs - - A font consists of a collection of glyphs together with other information (collectively, - the font tables) necessary to use those glyphs to present characters on some visual medium. - - The combination of the collection of glyphs and the font tables is called the font data. - - A font may supply substitution and positioning tables that can be used by a formatter - (text shaper) to re-order, combine and position a sequence of glyphs to form one or more - composite glyphs. - - The combining may be as simple as a ligature, or as complex as an indic syllable which - combines, usually with some re-ordering, multiple consonants and vowel glyphs. - - The tables may be language dependent, allowing the use of language appropriate letter forms. - - When a glyph, simple or composite, represents an indivisible unit for typesetting purposes, - it is know as a typographic character. - - Ligatures are an important feature of advance text layout. - - Some ligatures are discretionary while others (e.g. in Arabic) are required. - - The following explicit rules apply to ligature formation: - - Ligature formation should not be enabled when characters are in different DOM text nodes; - thus, characters separated by markup should not use ligatures. - - Ligature formation should not be enabled when characters are in different text chunks. - - Discretionary ligatures should not be used when the spacing between two characters is not - the same as the default space (e.g. when letter-spacing has a non-default value, - or text-align has a value of justify and text-justify has a value of distribute). - (See CSS Text Module Level 3, ([css-text-3]). - - SVG attributes such as ‘dx’, ‘textLength’, and ‘spacing’ (in ‘textPath’) that may reposition - typographic characters do not break discretionary ligatures. - - If discretionary ligatures are not desired - they can be turned off by using the font-variant-ligatures property. - - /* - When the effective letter-spacing between two characters is not zero - (due to either justification or non-zero computed ‘letter-spacing’), - user agents should not apply optional ligatures. - https://www.w3.org/TR/css-text-3/#letter-spacing-property - */ - final boolean allowOptionalLigatures = letterSpacing == 0 && - font.fontVariantLigatures == FontVariantLigatures.normal; - - /* - For OpenType fonts, discretionary ligatures include those enabled by - the liga, clig, dlig, hlig, and cala features; - required ligatures are found in the rlig feature. - https://svgwg.org/svg2-draft/text.html#FontsGlyphs - - http://dev.w3.org/csswg/css-fonts/#propdef-font-feature-settings - - https://www.microsoft.com/typography/otspec/featurelist.htm - https://www.microsoft.com/typography/otspec/featuretags.htm - https://www.microsoft.com/typography/otspec/features_pt.htm - https://www.microsoft.com/typography/otfntdev/arabicot/features.aspx - http://unifraktur.sourceforge.net/testcases/enable_opentype_features/ - https://en.wikipedia.org/wiki/List_of_typographic_features - http://ilovetypography.com/OpenType/opentype-features.html - https://www.typotheque.com/articles/opentype_features_in_css - https://practice.typekit.com/lesson/caring-about-opentype-features/ - http://stateofwebtype.com/ - - 6.12. Low-level font feature settings control: the font-feature-settings property - - Name: font-feature-settings - Value: normal | # - Initial: normal - Applies to: all elements - Inherited: yes - Percentages: N/A - Media: visual - Computed value: as specified - Animatable: no - - https://drafts.csswg.org/css-fonts-3/#default-features - - 7.1. Default features - - For OpenType fonts, user agents must enable the default features defined in the OpenType - documentation for a given script and writing mode. - - Required ligatures, common ligatures and contextual forms must be enabled by default - (OpenType features: rlig, liga, clig, calt), - along with localized forms (OpenType feature: locl), - and features required for proper display of composed characters and marks - (OpenType features: ccmp, mark, mkmk). - - These features must always be enabled, even when the value of the ‘font-variant’ and - ‘font-feature-settings’ properties is ‘normal’. - - Individual features are only disabled when explicitly overridden by the author, - as when ‘font-variant-ligatures’ is set to ‘no-common-ligatures’. - - TODO For handling complex scripts such as Arabic, Mongolian or Devanagari additional features - are required. - - TODO For upright text within vertical text runs, - vertical alternates (OpenType feature: vert) must be enabled. - */ - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - // String arabic = "'isol', 'fina', 'medi', 'init', 'rclt', 'mset', 'curs', "; - if (allowOptionalLigatures) { - paint.setFontFeatureSettings(defaultFeatures + additionalLigatures + font.fontFeatureSettings); - } else { - paint.setFontFeatureSettings(defaultFeatures + disableDiscretionaryLigatures + font.fontFeatureSettings); - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - paint.setFontVariationSettings(fontWeightTag + font.absoluteFontWeight + font.fontVariationSettings); - } - } - // OpenType.js font data - ReadableMap fontData = font.fontData; - - float[] advances = new float[length]; - paint.getTextWidths(line, advances); - - /* - This would give both advances and textMeasure in one call / looping over the text - double textMeasure = paint.getTextRunAdvances(line, 0, length, 0, length, true, advances, 0); - */ - /* - Determine the startpoint-on-the-path for the first glyph using attribute ‘startOffset’ - and property text-anchor. - - For text-anchor:start, startpoint-on-the-path is the point - on the path which represents the point on the path which is ‘startOffset’ distance - along the path from the start of the path, calculated using the user agent's distance - along the path algorithm. - - For text-anchor:middle, startpoint-on-the-path is the point - on the path which represents the point on the path which is [ ‘startOffset’ minus half - of the total advance values for all of the glyphs in the ‘textPath’ element ] distance - along the path from the start of the path, calculated using the user agent's distance - along the path algorithm. - - For text-anchor:end, startpoint-on-the-path is the point on - the path which represents the point on the path which is [ ‘startOffset’ minus the - total advance values for all of the glyphs in the ‘textPath’ element ]. - - Before rendering the first glyph, the horizontal component of the startpoint-on-the-path - is adjusted to take into account various horizontal alignment text properties and - attributes, such as a ‘dx’ attribute value on a ‘tspan’ element. - */ - final TextAnchor textAnchor = font.textAnchor; - TextView anchorRoot = getTextAnchorRoot(); - final double textMeasure = anchorRoot.getSubtreeTextChunksTotalAdvance(paint); - double offset = getTextAnchorOffset(textAnchor, textMeasure); - - int side = 1; - double startOfRendering = 0; - double endOfRendering = pathLength; - double fontSize = gc.getFontSize(); - boolean sharpMidLine = false; - if (hasTextPath) { - sharpMidLine = textPath.getMidLine() == TextPathMidLine.sharp; - /* - Name - side - Value - left | right - initial value - left - Animatable - yes - - Determines the side of the path the text is placed on - (relative to the path direction). - - Specifying a value of right effectively reverses the path. - - Added in SVG 2 to allow text either inside or outside closed subpaths - and basic shapes (e.g. rectangles, circles, and ellipses). - - Adding 'side' was resolved at the Sydney (2015) meeting. - */ - side = textPath.getSide() == TextPathSide.right ? -1 : 1; - /* - Name - startOffset - Value - | | - initial value - 0 - Animatable - yes - - An offset from the start of the path for the initial current text position, - calculated using the user agent's distance along the path algorithm, - after converting the path to the ‘textPath’ element's coordinate system. - - If a other than a percentage is given, then the ‘startOffset’ - represents a distance along the path measured in the current user coordinate - system for the ‘textPath’ element. - - If a percentage is given, then the ‘startOffset’ represents a percentage - distance along the entire path. Thus, startOffset="0%" indicates the start - point of the path and startOffset="100%" indicates the end point of the path. - - Negative values and values larger than the path length (e.g. 150%) are allowed. - - Any typographic characters with mid-points that are not on the path are not rendered - - For paths consisting of a single closed subpath (including an equivalent path for a - basic shape), typographic characters are rendered along one complete circuit of the - path. The text is aligned as determined by the text-anchor property to a position - along the path set by the ‘startOffset’ attribute. - - For the start (end) value, the text is rendered from the start (end) of the line - until the initial position along the path is reached again. - - For the middle, the text is rendered from the middle point in both directions until - a point on the path equal distance in both directions from the initial position on - the path is reached. - */ - final double absoluteStartOffset = getAbsoluteStartOffset(textPath.getStartOffset(), pathLength, fontSize); - offset += absoluteStartOffset; - if (isClosed) { - final double halfPathDistance = pathLength / 2; - startOfRendering = absoluteStartOffset + (textAnchor == TextAnchor.middle ? -halfPathDistance : 0); - endOfRendering = startOfRendering + pathLength; - } - /* - TextPathSpacing spacing = textPath.getSpacing(); - if (spacing == TextPathSpacing.auto) { - // Hmm, what to do here? - // https://svgwg.org/svg2-draft/text.html#TextPathElementSpacingAttribute - } - */ - } - - /* - Name - method - Value - align | stretch - initial value - align - Animatable - yes - Indicates the method by which text should be rendered along the path. - - A value of align indicates that the typographic character should be rendered using - simple 2×3 matrix transformations such that there is no stretching/warping of the - typographic characters. Typically, supplemental rotation, scaling and translation - transformations are done for each typographic characters to be rendered. - - As a result, with align, in fonts where the typographic characters are designed to be - connected (e.g., cursive fonts), the connections may not align properly when text is - rendered along a path. - - A value of stretch indicates that the typographic character outlines will be converted - into paths, and then all end points and control points will be adjusted to be along the - perpendicular vectors from the path, thereby stretching and possibly warping the glyphs. - - With this approach, connected typographic characters, such as in cursive scripts, - will maintain their connections. (Non-vertical straight path segments should be - converted to Bézier curves in such a way that horizontal straight paths have an - (approximately) constant offset from the path along which the typographic characters - are rendered.) - - TODO implement stretch - */ - - /* - Name Value Initial value Animatable - textLength | | See below yes - - The author's computation of the total sum of all of the advance values that correspond - to character data within this element, including the advance value on the glyph - (horizontal or vertical), the effect of properties letter-spacing and word-spacing and - adjustments due to attributes ‘dx’ and ‘dy’ on this ‘text’ or ‘tspan’ element or any - descendants. This value is used to calibrate the user agent's own calculations with - that of the author. - - The purpose of this attribute is to allow the author to achieve exact alignment, - in visual rendering order after any bidirectional reordering, for the first and - last rendered glyphs that correspond to this element; thus, for the last rendered - character (in visual rendering order after any bidirectional reordering), - any supplemental inter-character spacing beyond normal glyph advances are ignored - (in most cases) when the user agent determines the appropriate amount to expand/compress - the text string to fit within a length of ‘textLength’. - - If attribute ‘textLength’ is specified on a given element and also specified on an - ancestor, the adjustments on all character data within this element are controlled by - the value of ‘textLength’ on this element exclusively, with the possible side-effect - that the adjustment ratio for the contents of this element might be different than the - adjustment ratio used for other content that shares the same ancestor. The user agent - must assume that the total advance values for the other content within that ancestor is - the difference between the advance value on that ancestor and the advance value for - this element. - - This attribute is not intended for use to obtain effects such as shrinking or - expanding text. - - A negative value is an error (see Error processing). - - The ‘textLength’ attribute is only applied when the wrapping area is not defined by the - TODO shape-inside or the inline-size properties. It is also not applied for any ‘text’ or - TODO ‘tspan’ element that has forced line breaks (due to a white-space value of pre or - pre-line). - - If the attribute is not specified anywhere within a ‘text’ element, the effect is as if - the author's computation exactly matched the value calculated by the user agent; - thus, no advance adjustments are made. - */ - double scaleSpacingAndGlyphs = 1; - if (mTextLength != null) { - final double author = PropHelper.fromRelative(mTextLength, canvas.getWidth(), 0, mScale, fontSize); - if (author < 0) { - throw new IllegalArgumentException("Negative textLength value"); - } - switch (mLengthAdjust) { - default: - case spacing: - letterSpacing += (author - textMeasure) / (length - 1); - break; - case spacingAndGlyphs: - scaleSpacingAndGlyphs = author / textMeasure; - break; - } - } - final double scaledDirection = scaleSpacingAndGlyphs * side; - - /* - https://developer.mozilla.org/en/docs/Web/CSS/vertical-align - https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6bsln.html - https://www.microsoft.com/typography/otspec/base.htm - http://apike.ca/prog_svg_text_style.html - https://www.w3schools.com/tags/canvas_textbaseline.asp - http://vanseodesign.com/web-design/svg-text-baseline-alignment/ - https://iamvdo.me/en/blog/css-font-metrics-line-height-and-vertical-align - https://tympanus.net/codrops/css_reference/vertical-align/ - - https://svgwg.org/svg2-draft/text.html#AlignmentBaselineProperty - 11.10.2.6. The ‘alignment-baseline’ property - - This property is defined in the CSS Line Layout Module 3 specification. See 'alignment-baseline'. [css-inline-3] - https://drafts.csswg.org/css-inline/#propdef-alignment-baseline - - The vertical-align property shorthand should be preferred in new content. - - SVG 2 introduces some changes to the definition of this property. - In particular: the values 'auto', 'before-edge', and 'after-edge' have been removed. - For backwards compatibility, 'text-before-edge' should be mapped to 'text-top' and - 'text-after-edge' should be mapped to 'text-bottom'. - - Neither 'text-before-edge' nor 'text-after-edge' should be used with the vertical-align property. - */ - final Paint.FontMetrics fm = paint.getFontMetrics(); - final double descenderDepth = fm.descent; - final double bottom = descenderDepth + fm.leading; - final double ascenderHeight = -fm.ascent + fm.leading; - final double top = -fm.top; - final double totalHeight = top + bottom; - double baselineShift = 0; - String baselineShiftString = getBaselineShift(); - AlignmentBaseline baseline = getAlignmentBaseline(); - if (baseline != null) { - // TODO alignment-baseline, test / verify behavior - // TODO get per glyph baselines from font baseline table, for high-precision alignment - switch (baseline) { - // https://wiki.apache.org/xmlgraphics-fop/LineLayout/AlignmentHandling - default: - case baseline: - // Use the dominant baseline choice of the parent. - // Match the box’s corresponding baseline to that of its parent. - baselineShift = 0; - break; - - case textBottom: - case afterEdge: - case textAfterEdge: - // Match the bottom of the box to the bottom of the parent’s content area. - // text-after-edge = text-bottom - // text-after-edge = descender depth - baselineShift = -descenderDepth; - break; - - case alphabetic: - // Match the box’s alphabetic baseline to that of its parent. - // alphabetic = 0 - baselineShift = 0; - break; - - case ideographic: - // Match the box’s ideographic character face under-side baseline to that of its parent. - // ideographic = descender depth - baselineShift = -descenderDepth; - break; - - case middle: - // Align the vertical midpoint of the box with the baseline of the parent box plus half the x-height of the parent. - // middle = x height / 2 - Rect bounds = new Rect(); - // this will just retrieve the bounding rect for 'x' - paint.getTextBounds("x", 0, 1, bounds); - int xHeight = bounds.height(); - baselineShift = xHeight / 2.0; - break; - - case central: - // Match the box’s central baseline to the central baseline of its parent. - // central = (ascender height - descender depth) / 2 - baselineShift = (ascenderHeight - descenderDepth) / 2; - break; - - case mathematical: - // Match the box’s mathematical baseline to that of its parent. - // Hanging and mathematical baselines - // There are no obvious formulas to calculate the position of these baselines. - // At the time of writing FOP puts the hanging baseline at 80% of the ascender - // height and the mathematical baseline at 50%. - baselineShift = 0.5 * ascenderHeight; - break; - - case hanging: - baselineShift = 0.8 * ascenderHeight; - break; - - case textTop: - case beforeEdge: - case textBeforeEdge: - // Match the top of the box to the top of the parent’s content area. - // text-before-edge = text-top - // text-before-edge = ascender height - baselineShift = ascenderHeight; - break; - - case bottom: - // Align the top of the aligned subtree with the top of the line box. - baselineShift = bottom; - break; - - case center: - // Align the center of the aligned subtree with the center of the line box. - baselineShift = totalHeight / 2; - break; - - case top: - // Align the bottom of the aligned subtree with the bottom of the line box. - baselineShift = top; - break; - } - } - /* - 2.2.2. Alignment Shift: baseline-shift longhand - - This property specifies by how much the box is shifted up from its alignment point. - It does not apply when alignment-baseline is top or bottom. - - Authors should use the vertical-align shorthand instead of this property. - - Values have the following meanings: - - - Raise (positive value) or lower (negative value) by the specified length. - - Raise (positive value) or lower (negative value) by the specified percentage of the line-height. - TODO sub - Lower by the offset appropriate for subscripts of the parent’s box. - (The UA should use the parent’s font data to find this offset whenever possible.) - TODO super - Raise by the offset appropriate for superscripts of the parent’s box. - (The UA should use the parent’s font data to find this offset whenever possible.) - - User agents may additionally support the keyword baseline as computing to 0 - if is necessary for them to support legacy SVG content. - Issue: We would prefer to remove this, - and are looking for feedback from SVG user agents as to whether it’s necessary. - - https://www.w3.org/TR/css-inline-3/#propdef-baseline-shift - */ - if (baselineShiftString != null && !baselineShiftString.isEmpty()) { - switch (baseline) { - case top: - case bottom: - break; - - default: - switch (baselineShiftString) { - case "sub": - // TODO - if (fontData != null && fontData.hasKey("tables") && fontData.hasKey("unitsPerEm")) { - int unitsPerEm = fontData.getInt("unitsPerEm"); - ReadableMap tables = fontData.getMap("tables"); - if (tables.hasKey("os2")) { - ReadableMap os2 = tables.getMap("os2"); - if (os2.hasKey("ySubscriptYOffset")) { - double subOffset = os2.getDouble("ySubscriptYOffset"); - baselineShift += mScale * fontSize * subOffset / unitsPerEm; - } - } - } - break; - - case "super": - // TODO - if (fontData != null && fontData.hasKey("tables") && fontData.hasKey("unitsPerEm")) { - int unitsPerEm = fontData.getInt("unitsPerEm"); - ReadableMap tables = fontData.getMap("tables"); - if (tables.hasKey("os2")) { - ReadableMap os2 = tables.getMap("os2"); - if (os2.hasKey("ySuperscriptYOffset")) { - double superOffset = os2.getDouble("ySuperscriptYOffset"); - baselineShift -= mScale * fontSize * superOffset / unitsPerEm; - } - } - } - break; - - case "baseline": - break; - - default: - baselineShift -= PropHelper.fromRelative(baselineShiftString, mScale * fontSize, mScale, fontSize); - } - break; - } - } - - final Matrix start = new Matrix(); - final Matrix mid = new Matrix(); - final Matrix end = new Matrix(); - - final float[] startPointMatrixData = new float[9]; - final float[] endPointMatrixData = new float[9]; - - for (int index = 0; index < length; index++) { - char currentChar = chars[index]; - String current = String.valueOf(currentChar); - boolean alreadyRenderedGraphemeCluster = ligature[index]; - - /* - Determine the glyph's charwidth (i.e., the amount which the current text position - advances horizontally when the glyph is drawn using horizontal text layout). - */ - boolean hasLigature = false; - if (alreadyRenderedGraphemeCluster) { - current = ""; - } else { - int nextIndex = index; - while (++nextIndex < length) { - float nextWidth = advances[nextIndex]; - if (nextWidth > 0) { - break; - } - String nextLigature = current + chars[nextIndex]; - ligature[nextIndex] = true; - current = nextLigature; - hasLigature = true; - } - } - double charWidth = paint.measureText(current) * scaleSpacingAndGlyphs; - - /* - For each subsequent glyph, set a new startpoint-on-the-path as the previous - endpoint-on-the-path, but with appropriate adjustments taking into account - horizontal kerning tables in the font and current values of various attributes - and properties, including spacing properties (e.g. letter-spacing and word-spacing) - and ‘tspan’ elements with values provided for attributes ‘dx’ and ‘dy’. All - adjustments are calculated as distance adjustments along the path, calculated - using the user agent's distance along the path algorithm. - */ - if (autoKerning) { - double kerned = advances[index] * scaleSpacingAndGlyphs; - kerning = kerned - charWidth; - } - - boolean isWordSeparator = currentChar == ' '; - double wordSpace = isWordSeparator ? wordSpacing : 0; - double spacing = wordSpace + letterSpacing; - double advance = charWidth + spacing; - - double x = gc.nextX(alreadyRenderedGraphemeCluster ? 0 : kerning + advance); - double y = gc.nextY(); - double dx = gc.nextDeltaX(); - double dy = gc.nextDeltaY(); - double r = gc.nextRotation(); - - if (alreadyRenderedGraphemeCluster || isWordSeparator) { - // Skip rendering other grapheme clusters of ligatures (already rendered), - // But, make sure to increment index positions by making gc.next() calls. - continue; - } - - advance *= side; - charWidth *= side; - double cursor = offset + (x + dx) * side; - double startPoint = cursor - advance; - - if (hasTextPath) { - /* - Determine the point on the curve which is charwidth distance along the path from - the startpoint-on-the-path for this glyph, calculated using the user agent's - distance along the path algorithm. This point is the endpoint-on-the-path for - the glyph. - */ - double endPoint = startPoint + charWidth; - - /* - Determine the midpoint-on-the-path, which is the point on the path which is - "halfway" (user agents can choose either a distance calculation or a parametric - calculation) between the startpoint-on-the-path and the endpoint-on-the-path. - */ - double halfWay = charWidth / 2; - double midPoint = startPoint + halfWay; - - // Glyphs whose midpoint-on-the-path are off the path are not rendered. - if (midPoint > endOfRendering) { - continue; - } else if (midPoint < startOfRendering) { - continue; - } - - /* - Determine the glyph-midline, which is the vertical line in the glyph's - coordinate system that goes through the glyph's x-axis midpoint. - - Position the glyph such that the glyph-midline passes through - the midpoint-on-the-path and is perpendicular to the line - through the startpoint-on-the-path and the endpoint-on-the-path. - - TODO suggest adding a compatibility mid-line rendering attribute to textPath, - for a chrome/firefox/opera/safari compatible sharp text path rendering, - which doesn't bend text smoothly along a right angle curve, (like Edge does) - but keeps the mid-line orthogonal to the mid-point tangent at all times instead. - https://github.com/w3c/svgwg/issues/337 - */ - final int posAndTanFlags = POSITION_MATRIX_FLAG | TANGENT_MATRIX_FLAG; - if (sharpMidLine) { - pm.getMatrix((float) midPoint, mid, posAndTanFlags); - } else { - /* - In the calculation above, if either the startpoint-on-the-path - or the endpoint-on-the-path is off the end of the path, - then extend the path beyond its end points with a straight line - that is parallel to the tangent at the path at its end point - so that the midpoint-on-the-path can still be calculated. - - TODO suggest change in wording of svg spec: - so that the midpoint-on-the-path can still be calculated. - to - so that the angle of the glyph-midline to the x-axis can still be calculated. - or - so that the line through the startpoint-on-the-path and the - endpoint-on-the-path can still be calculated. - https://github.com/w3c/svgwg/issues/337#issuecomment-318056199 - */ - if (startPoint < 0) { - pm.getMatrix(0, start, posAndTanFlags); - start.preTranslate((float) startPoint, 0); - } else { - pm.getMatrix((float) startPoint, start, POSITION_MATRIX_FLAG); - } - - pm.getMatrix((float) midPoint, mid, POSITION_MATRIX_FLAG); - - if (endPoint > pathLength) { - pm.getMatrix((float) pathLength, end, posAndTanFlags); - end.preTranslate((float) (endPoint - pathLength), 0); - } else { - pm.getMatrix((float) endPoint, end, POSITION_MATRIX_FLAG); - } - - start.getValues(startPointMatrixData); - end.getValues(endPointMatrixData); - - double startX = startPointMatrixData[MTRANS_X]; - double startY = startPointMatrixData[MTRANS_Y]; - double endX = endPointMatrixData[MTRANS_X]; - double endY = endPointMatrixData[MTRANS_Y]; - - // line through the startpoint-on-the-path and the endpoint-on-the-path - double lineX = endX - startX; - double lineY = endY - startY; - - double glyphMidlineAngle = Math.atan2(lineY, lineX); - - mid.preRotate((float) (glyphMidlineAngle * radToDeg * side)); - } - - /* - Align the glyph vertically relative to the midpoint-on-the-path based on property - alignment-baseline and any specified values for attribute ‘dy’ on a ‘tspan’ element. - */ - mid.preTranslate((float) -halfWay, (float) (dy + baselineShift)); - mid.preScale((float) scaledDirection, (float) side); - mid.postTranslate(0, (float) y); - } else { - mid.setTranslate((float) startPoint, (float) (y + dy + baselineShift)); - } - - mid.preRotate((float) r); - - - Path glyph; - if (hasLigature) { - glyph = new Path(); - paint.getTextPath(current, 0, current.length(), 0, 0, glyph); - } else { - glyph = bag.getOrCreateAndCache(currentChar, current); - } - RectF bounds = new RectF(); - glyph.computeBounds(bounds, true); - float width = bounds.width(); - if (width == 0) { // Render unicode emoji - canvas.save(); - canvas.concat(mid); - emoji.add(current); - emojiTransforms.add(new Matrix(mid)); - canvas.drawText(current, 0, 0, paint); - canvas.restore(); - } else { - glyph.transform(mid); - path.addPath(glyph); - } - } - + double pathLength = 0; + PathMeasure pm = null; + boolean isClosed = false; + final boolean hasTextPath = textPath != null; + if (hasTextPath) { + pm = new PathMeasure(textPath.getTextPath(canvas, paint), false); + pathLength = pm.getLength(); + isClosed = pm.isClosed(); + if (pathLength == 0) { return path; + } } - private double getAbsoluteStartOffset(SVGLength startOffset, double distance, double fontSize) { - return PropHelper.fromRelative(startOffset, distance, 0, mScale, fontSize); + GlyphContext gc = getTextRootGlyphContext(); + FontData font = gc.getFont(); + applyTextPropertiesToPaint(paint, font); + GlyphPathBag bag = new GlyphPathBag(paint); + boolean[] ligature = new boolean[length]; + final char[] chars = line.toCharArray(); + + /* + * + * Three properties affect the space between characters and words: + * + * ‘kerning’ indicates whether the user agent should adjust inter-glyph spacing + * based on kerning tables that are included in the relevant font + * (i.e., enable auto-kerning) or instead disable auto-kerning + * and instead set inter-character spacing to a specific length (typically, zero). + * + * ‘letter-spacing’ indicates an amount of space that is to be added between text + * characters supplemental to any spacing due to the ‘kerning’ property. + * + * ‘word-spacing’ indicates the spacing behavior between words. + * + * Letter-spacing is applied after bidi reordering and is in addition to any word-spacing. + * Depending on the justification rules in effect, user agents may further increase + * or decrease the space between typographic character units in order to justify text. + * + * */ + double kerning = font.kerning; + double wordSpacing = font.wordSpacing; + double letterSpacing = font.letterSpacing; + final boolean autoKerning = !font.manualKerning; + + /* + 11.1.2. Fonts and glyphs + + A font consists of a collection of glyphs together with other information (collectively, + the font tables) necessary to use those glyphs to present characters on some visual medium. + + The combination of the collection of glyphs and the font tables is called the font data. + + A font may supply substitution and positioning tables that can be used by a formatter + (text shaper) to re-order, combine and position a sequence of glyphs to form one or more + composite glyphs. + + The combining may be as simple as a ligature, or as complex as an indic syllable which + combines, usually with some re-ordering, multiple consonants and vowel glyphs. + + The tables may be language dependent, allowing the use of language appropriate letter forms. + + When a glyph, simple or composite, represents an indivisible unit for typesetting purposes, + it is know as a typographic character. + + Ligatures are an important feature of advance text layout. + + Some ligatures are discretionary while others (e.g. in Arabic) are required. + + The following explicit rules apply to ligature formation: + + Ligature formation should not be enabled when characters are in different DOM text nodes; + thus, characters separated by markup should not use ligatures. + + Ligature formation should not be enabled when characters are in different text chunks. + + Discretionary ligatures should not be used when the spacing between two characters is not + the same as the default space (e.g. when letter-spacing has a non-default value, + or text-align has a value of justify and text-justify has a value of distribute). + (See CSS Text Module Level 3, ([css-text-3]). + + SVG attributes such as ‘dx’, ‘textLength’, and ‘spacing’ (in ‘textPath’) that may reposition + typographic characters do not break discretionary ligatures. + + If discretionary ligatures are not desired + they can be turned off by using the font-variant-ligatures property. + + /* + When the effective letter-spacing between two characters is not zero + (due to either justification or non-zero computed ‘letter-spacing’), + user agents should not apply optional ligatures. + https://www.w3.org/TR/css-text-3/#letter-spacing-property + */ + final boolean allowOptionalLigatures = + letterSpacing == 0 && font.fontVariantLigatures == FontVariantLigatures.normal; + + /* + For OpenType fonts, discretionary ligatures include those enabled by + the liga, clig, dlig, hlig, and cala features; + required ligatures are found in the rlig feature. + https://svgwg.org/svg2-draft/text.html#FontsGlyphs + + http://dev.w3.org/csswg/css-fonts/#propdef-font-feature-settings + + https://www.microsoft.com/typography/otspec/featurelist.htm + https://www.microsoft.com/typography/otspec/featuretags.htm + https://www.microsoft.com/typography/otspec/features_pt.htm + https://www.microsoft.com/typography/otfntdev/arabicot/features.aspx + http://unifraktur.sourceforge.net/testcases/enable_opentype_features/ + https://en.wikipedia.org/wiki/List_of_typographic_features + http://ilovetypography.com/OpenType/opentype-features.html + https://www.typotheque.com/articles/opentype_features_in_css + https://practice.typekit.com/lesson/caring-about-opentype-features/ + http://stateofwebtype.com/ + + 6.12. Low-level font feature settings control: the font-feature-settings property + + Name: font-feature-settings + Value: normal | # + Initial: normal + Applies to: all elements + Inherited: yes + Percentages: N/A + Media: visual + Computed value: as specified + Animatable: no + + https://drafts.csswg.org/css-fonts-3/#default-features + + 7.1. Default features + + For OpenType fonts, user agents must enable the default features defined in the OpenType + documentation for a given script and writing mode. + + Required ligatures, common ligatures and contextual forms must be enabled by default + (OpenType features: rlig, liga, clig, calt), + along with localized forms (OpenType feature: locl), + and features required for proper display of composed characters and marks + (OpenType features: ccmp, mark, mkmk). + + These features must always be enabled, even when the value of the ‘font-variant’ and + ‘font-feature-settings’ properties is ‘normal’. + + Individual features are only disabled when explicitly overridden by the author, + as when ‘font-variant-ligatures’ is set to ‘no-common-ligatures’. + + TODO For handling complex scripts such as Arabic, Mongolian or Devanagari additional features + are required. + + TODO For upright text within vertical text runs, + vertical alternates (OpenType feature: vert) must be enabled. + */ + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + // String arabic = "'isol', 'fina', 'medi', 'init', 'rclt', 'mset', 'curs', "; + if (allowOptionalLigatures) { + paint.setFontFeatureSettings( + defaultFeatures + additionalLigatures + font.fontFeatureSettings); + } else { + paint.setFontFeatureSettings( + defaultFeatures + disableDiscretionaryLigatures + font.fontFeatureSettings); + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + paint.setFontVariationSettings( + fontWeightTag + font.absoluteFontWeight + font.fontVariationSettings); + } + } + // OpenType.js font data + ReadableMap fontData = font.fontData; + + float[] advances = new float[length]; + paint.getTextWidths(line, advances); + + /* + This would give both advances and textMeasure in one call / looping over the text + double textMeasure = paint.getTextRunAdvances(line, 0, length, 0, length, true, advances, 0); + */ + /* + Determine the startpoint-on-the-path for the first glyph using attribute ‘startOffset’ + and property text-anchor. + + For text-anchor:start, startpoint-on-the-path is the point + on the path which represents the point on the path which is ‘startOffset’ distance + along the path from the start of the path, calculated using the user agent's distance + along the path algorithm. + + For text-anchor:middle, startpoint-on-the-path is the point + on the path which represents the point on the path which is [ ‘startOffset’ minus half + of the total advance values for all of the glyphs in the ‘textPath’ element ] distance + along the path from the start of the path, calculated using the user agent's distance + along the path algorithm. + + For text-anchor:end, startpoint-on-the-path is the point on + the path which represents the point on the path which is [ ‘startOffset’ minus the + total advance values for all of the glyphs in the ‘textPath’ element ]. + + Before rendering the first glyph, the horizontal component of the startpoint-on-the-path + is adjusted to take into account various horizontal alignment text properties and + attributes, such as a ‘dx’ attribute value on a ‘tspan’ element. + */ + final TextAnchor textAnchor = font.textAnchor; + TextView anchorRoot = getTextAnchorRoot(); + final double textMeasure = anchorRoot.getSubtreeTextChunksTotalAdvance(paint); + double offset = getTextAnchorOffset(textAnchor, textMeasure); + + int side = 1; + double startOfRendering = 0; + double endOfRendering = pathLength; + double fontSize = gc.getFontSize(); + boolean sharpMidLine = false; + if (hasTextPath) { + sharpMidLine = textPath.getMidLine() == TextPathMidLine.sharp; + /* + Name + side + Value + left | right + initial value + left + Animatable + yes + + Determines the side of the path the text is placed on + (relative to the path direction). + + Specifying a value of right effectively reverses the path. + + Added in SVG 2 to allow text either inside or outside closed subpaths + and basic shapes (e.g. rectangles, circles, and ellipses). + + Adding 'side' was resolved at the Sydney (2015) meeting. + */ + side = textPath.getSide() == TextPathSide.right ? -1 : 1; + /* + Name + startOffset + Value + | | + initial value + 0 + Animatable + yes + + An offset from the start of the path for the initial current text position, + calculated using the user agent's distance along the path algorithm, + after converting the path to the ‘textPath’ element's coordinate system. + + If a other than a percentage is given, then the ‘startOffset’ + represents a distance along the path measured in the current user coordinate + system for the ‘textPath’ element. + + If a percentage is given, then the ‘startOffset’ represents a percentage + distance along the entire path. Thus, startOffset="0%" indicates the start + point of the path and startOffset="100%" indicates the end point of the path. + + Negative values and values larger than the path length (e.g. 150%) are allowed. + + Any typographic characters with mid-points that are not on the path are not rendered + + For paths consisting of a single closed subpath (including an equivalent path for a + basic shape), typographic characters are rendered along one complete circuit of the + path. The text is aligned as determined by the text-anchor property to a position + along the path set by the ‘startOffset’ attribute. + + For the start (end) value, the text is rendered from the start (end) of the line + until the initial position along the path is reached again. + + For the middle, the text is rendered from the middle point in both directions until + a point on the path equal distance in both directions from the initial position on + the path is reached. + */ + final double absoluteStartOffset = + getAbsoluteStartOffset(textPath.getStartOffset(), pathLength, fontSize); + offset += absoluteStartOffset; + if (isClosed) { + final double halfPathDistance = pathLength / 2; + startOfRendering = + absoluteStartOffset + (textAnchor == TextAnchor.middle ? -halfPathDistance : 0); + endOfRendering = startOfRendering + pathLength; + } + /* + TextPathSpacing spacing = textPath.getSpacing(); + if (spacing == TextPathSpacing.auto) { + // Hmm, what to do here? + // https://svgwg.org/svg2-draft/text.html#TextPathElementSpacingAttribute + } + */ } - private double getTextAnchorOffset(TextAnchor textAnchor, double textMeasure) { - switch (textAnchor) { + /* + Name + method + Value + align | stretch + initial value + align + Animatable + yes + Indicates the method by which text should be rendered along the path. + + A value of align indicates that the typographic character should be rendered using + simple 2×3 matrix transformations such that there is no stretching/warping of the + typographic characters. Typically, supplemental rotation, scaling and translation + transformations are done for each typographic characters to be rendered. + + As a result, with align, in fonts where the typographic characters are designed to be + connected (e.g., cursive fonts), the connections may not align properly when text is + rendered along a path. + + A value of stretch indicates that the typographic character outlines will be converted + into paths, and then all end points and control points will be adjusted to be along the + perpendicular vectors from the path, thereby stretching and possibly warping the glyphs. + + With this approach, connected typographic characters, such as in cursive scripts, + will maintain their connections. (Non-vertical straight path segments should be + converted to Bézier curves in such a way that horizontal straight paths have an + (approximately) constant offset from the path along which the typographic characters + are rendered.) + + TODO implement stretch + */ + + /* + Name Value Initial value Animatable + textLength | | See below yes + + The author's computation of the total sum of all of the advance values that correspond + to character data within this element, including the advance value on the glyph + (horizontal or vertical), the effect of properties letter-spacing and word-spacing and + adjustments due to attributes ‘dx’ and ‘dy’ on this ‘text’ or ‘tspan’ element or any + descendants. This value is used to calibrate the user agent's own calculations with + that of the author. + + The purpose of this attribute is to allow the author to achieve exact alignment, + in visual rendering order after any bidirectional reordering, for the first and + last rendered glyphs that correspond to this element; thus, for the last rendered + character (in visual rendering order after any bidirectional reordering), + any supplemental inter-character spacing beyond normal glyph advances are ignored + (in most cases) when the user agent determines the appropriate amount to expand/compress + the text string to fit within a length of ‘textLength’. + + If attribute ‘textLength’ is specified on a given element and also specified on an + ancestor, the adjustments on all character data within this element are controlled by + the value of ‘textLength’ on this element exclusively, with the possible side-effect + that the adjustment ratio for the contents of this element might be different than the + adjustment ratio used for other content that shares the same ancestor. The user agent + must assume that the total advance values for the other content within that ancestor is + the difference between the advance value on that ancestor and the advance value for + this element. + + This attribute is not intended for use to obtain effects such as shrinking or + expanding text. + + A negative value is an error (see Error processing). + + The ‘textLength’ attribute is only applied when the wrapping area is not defined by the + TODO shape-inside or the inline-size properties. It is also not applied for any ‘text’ or + TODO ‘tspan’ element that has forced line breaks (due to a white-space value of pre or + pre-line). + + If the attribute is not specified anywhere within a ‘text’ element, the effect is as if + the author's computation exactly matched the value calculated by the user agent; + thus, no advance adjustments are made. + */ + double scaleSpacingAndGlyphs = 1; + if (mTextLength != null) { + final double author = + PropHelper.fromRelative(mTextLength, canvas.getWidth(), 0, mScale, fontSize); + if (author < 0) { + throw new IllegalArgumentException("Negative textLength value"); + } + switch (mLengthAdjust) { + default: + case spacing: + letterSpacing += (author - textMeasure) / (length - 1); + break; + case spacingAndGlyphs: + scaleSpacingAndGlyphs = author / textMeasure; + break; + } + } + final double scaledDirection = scaleSpacingAndGlyphs * side; + + /* + https://developer.mozilla.org/en/docs/Web/CSS/vertical-align + https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6bsln.html + https://www.microsoft.com/typography/otspec/base.htm + http://apike.ca/prog_svg_text_style.html + https://www.w3schools.com/tags/canvas_textbaseline.asp + http://vanseodesign.com/web-design/svg-text-baseline-alignment/ + https://iamvdo.me/en/blog/css-font-metrics-line-height-and-vertical-align + https://tympanus.net/codrops/css_reference/vertical-align/ + + https://svgwg.org/svg2-draft/text.html#AlignmentBaselineProperty + 11.10.2.6. The ‘alignment-baseline’ property + + This property is defined in the CSS Line Layout Module 3 specification. See 'alignment-baseline'. [css-inline-3] + https://drafts.csswg.org/css-inline/#propdef-alignment-baseline + + The vertical-align property shorthand should be preferred in new content. + + SVG 2 introduces some changes to the definition of this property. + In particular: the values 'auto', 'before-edge', and 'after-edge' have been removed. + For backwards compatibility, 'text-before-edge' should be mapped to 'text-top' and + 'text-after-edge' should be mapped to 'text-bottom'. + + Neither 'text-before-edge' nor 'text-after-edge' should be used with the vertical-align property. + */ + final Paint.FontMetrics fm = paint.getFontMetrics(); + final double descenderDepth = fm.descent; + final double bottom = descenderDepth + fm.leading; + final double ascenderHeight = -fm.ascent + fm.leading; + final double top = -fm.top; + final double totalHeight = top + bottom; + double baselineShift = 0; + String baselineShiftString = getBaselineShift(); + AlignmentBaseline baseline = getAlignmentBaseline(); + if (baseline != null) { + // TODO alignment-baseline, test / verify behavior + // TODO get per glyph baselines from font baseline table, for high-precision alignment + switch (baseline) { + // https://wiki.apache.org/xmlgraphics-fop/LineLayout/AlignmentHandling + default: + case baseline: + // Use the dominant baseline choice of the parent. + // Match the box’s corresponding baseline to that of its parent. + baselineShift = 0; + break; + + case textBottom: + case afterEdge: + case textAfterEdge: + // Match the bottom of the box to the bottom of the parent’s content area. + // text-after-edge = text-bottom + // text-after-edge = descender depth + baselineShift = -descenderDepth; + break; + + case alphabetic: + // Match the box’s alphabetic baseline to that of its parent. + // alphabetic = 0 + baselineShift = 0; + break; + + case ideographic: + // Match the box’s ideographic character face under-side baseline to that of its parent. + // ideographic = descender depth + baselineShift = -descenderDepth; + break; + + case middle: + // Align the vertical midpoint of the box with the baseline of the parent box plus half + // the x-height of the parent. + // middle = x height / 2 + Rect bounds = new Rect(); + // this will just retrieve the bounding rect for 'x' + paint.getTextBounds("x", 0, 1, bounds); + int xHeight = bounds.height(); + baselineShift = xHeight / 2.0; + break; + + case central: + // Match the box’s central baseline to the central baseline of its parent. + // central = (ascender height - descender depth) / 2 + baselineShift = (ascenderHeight - descenderDepth) / 2; + break; + + case mathematical: + // Match the box’s mathematical baseline to that of its parent. + // Hanging and mathematical baselines + // There are no obvious formulas to calculate the position of these baselines. + // At the time of writing FOP puts the hanging baseline at 80% of the ascender + // height and the mathematical baseline at 50%. + baselineShift = 0.5 * ascenderHeight; + break; + + case hanging: + baselineShift = 0.8 * ascenderHeight; + break; + + case textTop: + case beforeEdge: + case textBeforeEdge: + // Match the top of the box to the top of the parent’s content area. + // text-before-edge = text-top + // text-before-edge = ascender height + baselineShift = ascenderHeight; + break; + + case bottom: + // Align the top of the aligned subtree with the top of the line box. + baselineShift = bottom; + break; + + case center: + // Align the center of the aligned subtree with the center of the line box. + baselineShift = totalHeight / 2; + break; + + case top: + // Align the bottom of the aligned subtree with the bottom of the line box. + baselineShift = top; + break; + } + } + /* + 2.2.2. Alignment Shift: baseline-shift longhand + + This property specifies by how much the box is shifted up from its alignment point. + It does not apply when alignment-baseline is top or bottom. + + Authors should use the vertical-align shorthand instead of this property. + + Values have the following meanings: + + + Raise (positive value) or lower (negative value) by the specified length. + + Raise (positive value) or lower (negative value) by the specified percentage of the line-height. + TODO sub + Lower by the offset appropriate for subscripts of the parent’s box. + (The UA should use the parent’s font data to find this offset whenever possible.) + TODO super + Raise by the offset appropriate for superscripts of the parent’s box. + (The UA should use the parent’s font data to find this offset whenever possible.) + + User agents may additionally support the keyword baseline as computing to 0 + if is necessary for them to support legacy SVG content. + Issue: We would prefer to remove this, + and are looking for feedback from SVG user agents as to whether it’s necessary. + + https://www.w3.org/TR/css-inline-3/#propdef-baseline-shift + */ + if (baselineShiftString != null && !baselineShiftString.isEmpty()) { + switch (baseline) { + case top: + case bottom: + break; + + default: + switch (baselineShiftString) { + case "sub": + // TODO + if (fontData != null && fontData.hasKey("tables") && fontData.hasKey("unitsPerEm")) { + int unitsPerEm = fontData.getInt("unitsPerEm"); + ReadableMap tables = fontData.getMap("tables"); + if (tables.hasKey("os2")) { + ReadableMap os2 = tables.getMap("os2"); + if (os2.hasKey("ySubscriptYOffset")) { + double subOffset = os2.getDouble("ySubscriptYOffset"); + baselineShift += mScale * fontSize * subOffset / unitsPerEm; + } + } + } + break; + + case "super": + // TODO + if (fontData != null && fontData.hasKey("tables") && fontData.hasKey("unitsPerEm")) { + int unitsPerEm = fontData.getInt("unitsPerEm"); + ReadableMap tables = fontData.getMap("tables"); + if (tables.hasKey("os2")) { + ReadableMap os2 = tables.getMap("os2"); + if (os2.hasKey("ySuperscriptYOffset")) { + double superOffset = os2.getDouble("ySuperscriptYOffset"); + baselineShift -= mScale * fontSize * superOffset / unitsPerEm; + } + } + } + break; + + case "baseline": + break; + default: - case start: - return 0; - - case middle: - return -textMeasure / 2; - - case end: - return -textMeasure; - } + baselineShift -= + PropHelper.fromRelative(baselineShiftString, mScale * fontSize, mScale, fontSize); + } + break; + } } - private void applyTextPropertiesToPaint(Paint paint, FontData font) { - boolean isBold = font.fontWeight == FontWeight.Bold || font.absoluteFontWeight >= 550; - boolean isItalic = font.fontStyle == FontStyle.italic; + final Matrix start = new Matrix(); + final Matrix mid = new Matrix(); + final Matrix end = new Matrix(); - int style; - if (isBold && isItalic) { - style = Typeface.BOLD_ITALIC; - } else if (isBold) { - style = Typeface.BOLD; - } else if (isItalic) { - style = Typeface.ITALIC; + final float[] startPointMatrixData = new float[9]; + final float[] endPointMatrixData = new float[9]; + + for (int index = 0; index < length; index++) { + char currentChar = chars[index]; + String current = String.valueOf(currentChar); + boolean alreadyRenderedGraphemeCluster = ligature[index]; + + /* + Determine the glyph's charwidth (i.e., the amount which the current text position + advances horizontally when the glyph is drawn using horizontal text layout). + */ + boolean hasLigature = false; + if (alreadyRenderedGraphemeCluster) { + current = ""; + } else { + int nextIndex = index; + while (++nextIndex < length) { + float nextWidth = advances[nextIndex]; + if (nextWidth > 0) { + break; + } + String nextLigature = current + chars[nextIndex]; + ligature[nextIndex] = true; + current = nextLigature; + hasLigature = true; + } + } + double charWidth = paint.measureText(current) * scaleSpacingAndGlyphs; + + /* + For each subsequent glyph, set a new startpoint-on-the-path as the previous + endpoint-on-the-path, but with appropriate adjustments taking into account + horizontal kerning tables in the font and current values of various attributes + and properties, including spacing properties (e.g. letter-spacing and word-spacing) + and ‘tspan’ elements with values provided for attributes ‘dx’ and ‘dy’. All + adjustments are calculated as distance adjustments along the path, calculated + using the user agent's distance along the path algorithm. + */ + if (autoKerning) { + double kerned = advances[index] * scaleSpacingAndGlyphs; + kerning = kerned - charWidth; + } + + boolean isWordSeparator = currentChar == ' '; + double wordSpace = isWordSeparator ? wordSpacing : 0; + double spacing = wordSpace + letterSpacing; + double advance = charWidth + spacing; + + double x = gc.nextX(alreadyRenderedGraphemeCluster ? 0 : kerning + advance); + double y = gc.nextY(); + double dx = gc.nextDeltaX(); + double dy = gc.nextDeltaY(); + double r = gc.nextRotation(); + + if (alreadyRenderedGraphemeCluster || isWordSeparator) { + // Skip rendering other grapheme clusters of ligatures (already rendered), + // But, make sure to increment index positions by making gc.next() calls. + continue; + } + + advance *= side; + charWidth *= side; + double cursor = offset + (x + dx) * side; + double startPoint = cursor - advance; + + if (hasTextPath) { + /* + Determine the point on the curve which is charwidth distance along the path from + the startpoint-on-the-path for this glyph, calculated using the user agent's + distance along the path algorithm. This point is the endpoint-on-the-path for + the glyph. + */ + double endPoint = startPoint + charWidth; + + /* + Determine the midpoint-on-the-path, which is the point on the path which is + "halfway" (user agents can choose either a distance calculation or a parametric + calculation) between the startpoint-on-the-path and the endpoint-on-the-path. + */ + double halfWay = charWidth / 2; + double midPoint = startPoint + halfWay; + + // Glyphs whose midpoint-on-the-path are off the path are not rendered. + if (midPoint > endOfRendering) { + continue; + } else if (midPoint < startOfRendering) { + continue; + } + + /* + Determine the glyph-midline, which is the vertical line in the glyph's + coordinate system that goes through the glyph's x-axis midpoint. + + Position the glyph such that the glyph-midline passes through + the midpoint-on-the-path and is perpendicular to the line + through the startpoint-on-the-path and the endpoint-on-the-path. + + TODO suggest adding a compatibility mid-line rendering attribute to textPath, + for a chrome/firefox/opera/safari compatible sharp text path rendering, + which doesn't bend text smoothly along a right angle curve, (like Edge does) + but keeps the mid-line orthogonal to the mid-point tangent at all times instead. + https://github.com/w3c/svgwg/issues/337 + */ + final int posAndTanFlags = POSITION_MATRIX_FLAG | TANGENT_MATRIX_FLAG; + if (sharpMidLine) { + pm.getMatrix((float) midPoint, mid, posAndTanFlags); } else { - style = Typeface.NORMAL; + /* + In the calculation above, if either the startpoint-on-the-path + or the endpoint-on-the-path is off the end of the path, + then extend the path beyond its end points with a straight line + that is parallel to the tangent at the path at its end point + so that the midpoint-on-the-path can still be calculated. + + TODO suggest change in wording of svg spec: + so that the midpoint-on-the-path can still be calculated. + to + so that the angle of the glyph-midline to the x-axis can still be calculated. + or + so that the line through the startpoint-on-the-path and the + endpoint-on-the-path can still be calculated. + https://github.com/w3c/svgwg/issues/337#issuecomment-318056199 + */ + if (startPoint < 0) { + pm.getMatrix(0, start, posAndTanFlags); + start.preTranslate((float) startPoint, 0); + } else { + pm.getMatrix((float) startPoint, start, POSITION_MATRIX_FLAG); + } + + pm.getMatrix((float) midPoint, mid, POSITION_MATRIX_FLAG); + + if (endPoint > pathLength) { + pm.getMatrix((float) pathLength, end, posAndTanFlags); + end.preTranslate((float) (endPoint - pathLength), 0); + } else { + pm.getMatrix((float) endPoint, end, POSITION_MATRIX_FLAG); + } + + start.getValues(startPointMatrixData); + end.getValues(endPointMatrixData); + + double startX = startPointMatrixData[MTRANS_X]; + double startY = startPointMatrixData[MTRANS_Y]; + double endX = endPointMatrixData[MTRANS_X]; + double endY = endPointMatrixData[MTRANS_Y]; + + // line through the startpoint-on-the-path and the endpoint-on-the-path + double lineX = endX - startX; + double lineY = endY - startY; + + double glyphMidlineAngle = Math.atan2(lineY, lineX); + + mid.preRotate((float) (glyphMidlineAngle * radToDeg * side)); } - Typeface typeface = null; - int weight = font.absoluteFontWeight; - final String fontFamily = font.fontFamily; - if (fontFamily != null && fontFamily.length() > 0) { - String otfpath = FONTS + fontFamily + OTF; - String ttfpath = FONTS + fontFamily + TTF; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - Typeface.Builder builder = new Typeface.Builder(assets, otfpath); - builder.setFontVariationSettings("'wght' " + weight + font.fontVariationSettings); - builder.setWeight(weight); - builder.setItalic(isItalic); - typeface = builder.build(); - if (typeface == null) { - builder = new Typeface.Builder(assets, ttfpath); - builder.setFontVariationSettings("'wght' " + weight + font.fontVariationSettings); - builder.setWeight(weight); - builder.setItalic(isItalic); - typeface = builder.build(); - } - } else { - try { - typeface = Typeface.createFromAsset(assets, otfpath); - typeface = Typeface.create(typeface, style); - } catch (Exception ignored) { - try { - typeface = Typeface.createFromAsset(assets, ttfpath); - typeface = Typeface.create(typeface, style); - } catch (Exception ignored2) { - } - } - } - } + /* + Align the glyph vertically relative to the midpoint-on-the-path based on property + alignment-baseline and any specified values for attribute ‘dy’ on a ‘tspan’ element. + */ + mid.preTranslate((float) -halfWay, (float) (dy + baselineShift)); + mid.preScale((float) scaledDirection, (float) side); + mid.postTranslate(0, (float) y); + } else { + mid.setTranslate((float) startPoint, (float) (y + dy + baselineShift)); + } + mid.preRotate((float) r); + + Path glyph; + if (hasLigature) { + glyph = new Path(); + paint.getTextPath(current, 0, current.length(), 0, 0, glyph); + } else { + glyph = bag.getOrCreateAndCache(currentChar, current); + } + RectF bounds = new RectF(); + glyph.computeBounds(bounds, true); + float width = bounds.width(); + if (width == 0) { // Render unicode emoji + canvas.save(); + canvas.concat(mid); + emoji.add(current); + emojiTransforms.add(new Matrix(mid)); + canvas.drawText(current, 0, 0, paint); + canvas.restore(); + } else { + glyph.transform(mid); + path.addPath(glyph); + } + } + + return path; + } + + private double getAbsoluteStartOffset(SVGLength startOffset, double distance, double fontSize) { + return PropHelper.fromRelative(startOffset, distance, 0, mScale, fontSize); + } + + private double getTextAnchorOffset(TextAnchor textAnchor, double textMeasure) { + switch (textAnchor) { + default: + case start: + return 0; + + case middle: + return -textMeasure / 2; + + case end: + return -textMeasure; + } + } + + private void applyTextPropertiesToPaint(Paint paint, FontData font) { + boolean isBold = font.fontWeight == FontWeight.Bold || font.absoluteFontWeight >= 550; + boolean isItalic = font.fontStyle == FontStyle.italic; + + int style; + if (isBold && isItalic) { + style = Typeface.BOLD_ITALIC; + } else if (isBold) { + style = Typeface.BOLD; + } else if (isItalic) { + style = Typeface.ITALIC; + } else { + style = Typeface.NORMAL; + } + + Typeface typeface = null; + int weight = font.absoluteFontWeight; + final String fontFamily = font.fontFamily; + if (fontFamily != null && fontFamily.length() > 0) { + String otfpath = FONTS + fontFamily + OTF; + String ttfpath = FONTS + fontFamily + TTF; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + Typeface.Builder builder = new Typeface.Builder(assets, otfpath); + builder.setFontVariationSettings("'wght' " + weight + font.fontVariationSettings); + builder.setWeight(weight); + builder.setItalic(isItalic); + typeface = builder.build(); if (typeface == null) { - try { - typeface = ReactFontManager.getInstance().getTypeface(fontFamily, style, assets); - } catch (Exception ignored) { - } + builder = new Typeface.Builder(assets, ttfpath); + builder.setFontVariationSettings("'wght' " + weight + font.fontVariationSettings); + builder.setWeight(weight); + builder.setItalic(isItalic); + typeface = builder.build(); } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - typeface = Typeface.create(typeface, weight, isItalic); - } - - paint.setLinearText(true); - paint.setSubpixelText(true); - paint.setTypeface(typeface); - paint.setTextSize((float) (font.fontSize * mScale)); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - paint.setLetterSpacing(0); + } else { + try { + typeface = Typeface.createFromAsset(assets, otfpath); + typeface = Typeface.create(typeface, style); + } catch (Exception ignored) { + try { + typeface = Typeface.createFromAsset(assets, ttfpath); + typeface = Typeface.create(typeface, style); + } catch (Exception ignored2) { + } } + } } - private void setupTextPath() { - ViewParent parent = getParent(); - - while (parent != null) { - if (parent.getClass() == TextPathView.class) { - textPath = (TextPathView) parent; - break; - } else if (!(parent instanceof TextView)) { - break; - } - - parent = parent.getParent(); - } + if (typeface == null) { + try { + typeface = ReactFontManager.getInstance().getTypeface(fontFamily, style, assets); + } catch (Exception ignored) { + } } - @Override - int hitTest(final float[] src) { - if (mContent == null) { - return super.hitTest(src); - } - if (mPath == null || !mInvertible || !mTransformInvertible) { - return -1; - } - - float[] dst = new float[2]; - mInvMatrix.mapPoints(dst, src); - mInvTransform.mapPoints(dst); - int x = Math.round(dst[0]); - int y = Math.round(dst[1]); - - initBounds(); - - if ( - (mRegion == null || !mRegion.contains(x, y)) && - (mStrokeRegion == null || !mStrokeRegion.contains(x, y)) - ) { - return -1; - } - - Path clipPath = getClipPath(); - if (clipPath != null) { - if (!mClipRegion.contains(x, y)) { - return -1; - } - } - - return getId(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + typeface = Typeface.create(typeface, weight, isItalic); } + + paint.setLinearText(true); + paint.setSubpixelText(true); + paint.setTypeface(typeface); + paint.setTextSize((float) (font.fontSize * mScale)); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + paint.setLetterSpacing(0); + } + } + + private void setupTextPath() { + ViewParent parent = getParent(); + + while (parent != null) { + if (parent.getClass() == TextPathView.class) { + textPath = (TextPathView) parent; + break; + } else if (!(parent instanceof TextView)) { + break; + } + + parent = parent.getParent(); + } + } + + @Override + int hitTest(final float[] src) { + if (mContent == null) { + return super.hitTest(src); + } + if (mPath == null || !mInvertible || !mTransformInvertible) { + return -1; + } + + float[] dst = new float[2]; + mInvMatrix.mapPoints(dst, src); + mInvTransform.mapPoints(dst); + int x = Math.round(dst[0]); + int y = Math.round(dst[1]); + + initBounds(); + + if ((mRegion == null || !mRegion.contains(x, y)) + && (mStrokeRegion == null || !mStrokeRegion.contains(x, y))) { + return -1; + } + + Path clipPath = getClipPath(); + if (clipPath != null) { + if (!mClipRegion.contains(x, y)) { + return -1; + } + } + + return getId(); + } } diff --git a/android/src/main/java/com/horcrux/svg/TextLayoutAlgorithm.java b/android/src/main/java/com/horcrux/svg/TextLayoutAlgorithm.java index baeca101..b3328df5 100644 --- a/android/src/main/java/com/horcrux/svg/TextLayoutAlgorithm.java +++ b/android/src/main/java/com/horcrux/svg/TextLayoutAlgorithm.java @@ -2,1302 +2,1293 @@ package com.horcrux.svg; // TODO implement https://www.w3.org/TR/SVG2/text.html#TextLayoutAlgorithm -import android.graphics.Path; -import android.graphics.PathMeasure; -import android.graphics.PointF; -import android.view.View; - -import java.util.ArrayList; - import static com.horcrux.svg.TextProperties.Direction; import static com.horcrux.svg.TextProperties.TextAnchor; import static com.horcrux.svg.TextProperties.TextPathSide; +import android.graphics.Path; +import android.graphics.PathMeasure; +import android.graphics.PointF; +import android.view.View; +import java.util.ArrayList; + @SuppressWarnings("ALL") class TextLayoutAlgorithm { - class CharacterInformation { - int index; - double x = 0; - double y = 0; - double advance; - char character; - double rotate = 0; - TextView element; - boolean hidden = false; - boolean middle = false; - boolean resolved = false; - boolean xSpecified = false; - boolean ySpecified = false; - boolean addressable = true; - boolean anchoredChunk = false; - boolean rotateSpecified = false; - boolean firstCharacterInResolvedDescendant = false; + class CharacterInformation { + int index; + double x = 0; + double y = 0; + double advance; + char character; + double rotate = 0; + TextView element; + boolean hidden = false; + boolean middle = false; + boolean resolved = false; + boolean xSpecified = false; + boolean ySpecified = false; + boolean addressable = true; + boolean anchoredChunk = false; + boolean rotateSpecified = false; + boolean firstCharacterInResolvedDescendant = false; - CharacterInformation(int index, char c) { - this.index = index; - this.character = c; - } + CharacterInformation(int index, char c) { + this.index = index; + this.character = c; } + } - class LayoutInput { - TextView text; - boolean horizontal; + class LayoutInput { + TextView text; + boolean horizontal; + } + + private void getSubTreeTypographicCharacterPositions( + ArrayList inTextPath, + ArrayList subtree, + StringBuilder line, + View node, + TextPathView textPath) { + if (node instanceof TSpanView) { + final TSpanView tSpanView = (TSpanView) node; + String content = tSpanView.mContent; + if (content == null) { + for (int i = 0; i < tSpanView.getChildCount(); i++) { + getSubTreeTypographicCharacterPositions( + inTextPath, subtree, line, tSpanView.getChildAt(i), textPath); + } + } else { + for (int i = 0; i < content.length(); i++) { + subtree.add(tSpanView); + inTextPath.add(textPath); + } + line.append(content); + } + } else { + textPath = node instanceof TextPathView ? (TextPathView) node : textPath; + for (int i = 0; i < textPath.getChildCount(); i++) { + getSubTreeTypographicCharacterPositions( + inTextPath, subtree, line, textPath.getChildAt(i), textPath); + } } + } - private void getSubTreeTypographicCharacterPositions( - ArrayList inTextPath, - ArrayList subtree, - StringBuilder line, - View node, - TextPathView textPath - ) { - if (node instanceof TSpanView) { - final TSpanView tSpanView = (TSpanView) node; - String content = tSpanView.mContent; - if (content == null) { - for (int i = 0; i < tSpanView.getChildCount(); i++) { - getSubTreeTypographicCharacterPositions(inTextPath, subtree, line, tSpanView.getChildAt(i), textPath); - } - } else { - for (int i = 0; i < content.length(); i++) { - subtree.add(tSpanView); - inTextPath.add(textPath); - } - line.append(content); - } - } else { - textPath = node instanceof TextPathView ? (TextPathView) node : textPath; - for (int i = 0; i < textPath.getChildCount(); i++) { - getSubTreeTypographicCharacterPositions(inTextPath, subtree, line, textPath.getChildAt(i), textPath); - } - } + CharacterInformation[] layoutText(LayoutInput layoutInput) { + /* + Setup + + Let root be the result of generating + typographic character positions for the + ‘text’ element and its subtree, laid out as if it + were an absolutely positioned element. + + This will be a single line of text unless the + white-space property causes line breaks. + */ + TextView text = layoutInput.text; + StringBuilder line = new StringBuilder(); + ArrayList subtree = new ArrayList<>(); + ArrayList inTextPath = new ArrayList<>(); + getSubTreeTypographicCharacterPositions(inTextPath, subtree, line, text, null); + final char[] root = line.toString().toCharArray(); + /* + Let count be the number of DOM characters + within the ‘text’ element's subtree. + */ + int count = root.length; + /* + + Let result be an array of length count + whose entries contain the per-character information described + above. Each entry is initialized as follows: + + its global index number equal to its position in the array, + its "x" coordinate set to "unspecified", + its "y" coordinate set to "unspecified", + its "rotate" coordinate set to "unspecified", + its "hidden" flag is false, + its "addressable" flag is true, + its "middle" flag is false, + its "anchored chunk" flag is false. + */ + final CharacterInformation[] result = new CharacterInformation[count]; + for (int i = 0; i < count; i++) { + result[i] = new CharacterInformation(i, root[i]); } - - CharacterInformation[] layoutText(LayoutInput layoutInput) { -/* - Setup - - Let root be the result of generating - typographic character positions for the - ‘text’ element and its subtree, laid out as if it - were an absolutely positioned element. - - This will be a single line of text unless the - white-space property causes line breaks. -*/ - TextView text = layoutInput.text; - StringBuilder line = new StringBuilder(); - ArrayList subtree = new ArrayList<>(); - ArrayList inTextPath = new ArrayList<>(); - getSubTreeTypographicCharacterPositions(inTextPath, subtree, line, text, null); - final char[] root = line.toString().toCharArray(); -/* - Let count be the number of DOM characters - within the ‘text’ element's subtree. -*/ - int count = root.length; -/* - - Let result be an array of length count - whose entries contain the per-character information described - above. Each entry is initialized as follows: - - its global index number equal to its position in the array, - its "x" coordinate set to "unspecified", - its "y" coordinate set to "unspecified", - its "rotate" coordinate set to "unspecified", - its "hidden" flag is false, - its "addressable" flag is true, - its "middle" flag is false, - its "anchored chunk" flag is false. -*/ - final CharacterInformation[] result = new CharacterInformation[count]; - for (int i = 0; i < count; i++) { - result[i] = new CharacterInformation(i, root[i]); - } -/* - If result is empty, then return result. -*/ - if (count == 0) { - return result; - } -/* - - Let CSS_positions be an array of length - count whose entries will be filled with the - x and y positions of the corresponding - typographic character in root. The array - entries are initialized to (0, 0). -*/ - PointF[] CSS_positions = new PointF[count]; - for (int i = 0; i < count; i++) { - CSS_positions[i] = new PointF(0, 0); - } -/* - Let "horizontal" be a flag, true if the writing mode of ‘text’ - is horizontal, false otherwise. -*/ - final boolean horizontal = true; -/* - Set flags and assign initial positions - - For each array element with index i in - result: -*/ - for (int i = 0; i < count; i++) { -/* - TODO Set addressable to false if the character at index i was: - - part of the text content of a non-rendered element - - discarded during layout due to being a - collapsed - white space character, a soft hyphen character, or a - bidi control character; or - - - discarded during layout due to being a - collapsed - segment break; or - - - trimmed - from the start or end of a line. - - Since there is collapsible white space not addressable by glyph - positioning attributes in the following ‘text’ element - (with a standard font), the "B" glyph will be placed at x=300. - - - A - B - - - This is because the white space before the "A", and all but one white space - character between the "A" and "B", is collapsed away or trimmed. - -*/ - result[i].addressable = true; -/* - - Set middle to true if the character at index i - TODO is the second or later character that corresponds to a typographic character. -*/ - result[i].middle = false; -/* - - TODO If the character at index i corresponds to a typographic character at the beginning of a line, then set the "anchored - chunk" flag of result[i] to true. - - This ensures chunks shifted by text-anchor do not - span multiple lines. -*/ - result[i].anchoredChunk = i == 0; -/* - - If addressable is true and middle is false then - set CSS_positions[i] to the position of the - TODO corresponding typographic character as determined by the CSS - renderer. Otherwise, if i > 0, then set - CSS_positions[i] = - CSS_positions[i − 1] - -*/ - if (result[i].addressable && !result[i].middle) { - CSS_positions[i].set(0, 0); - } else if (i > 0) { - CSS_positions[i].set(CSS_positions[i - 1]); - } - } -/* - - Resolve character positioning - - Position adjustments (e.g values in a ‘x’ attribute) - specified by a node apply to all characters in that node including - characters in the node's descendants. Adjustments specified in - descendant nodes, however, override adjustments from ancestor - nodes. This section resolves which adjustments are to be applied to - which characters. It also directly sets the rotate coordinate - of result. - - Set up: - - Let resolve_x, resolve_y, - resolve_dx, and resolve_dy be arrays of - length count whose entries are all initialized - to "unspecified". -*/ - String[] resolve_x = new String[count]; - String[] resolve_y = new String[count]; - String[] resolve_dx = new String[count]; - String[] resolve_dy = new String[count]; -/* - - Set "in_text_path" flag false. - - This flag will allow ‘y’ (‘x’) - attribute values to be ignored for horizontal (vertical) - text inside ‘textPath’ elements. -*/ - boolean in_text_path = false; -/* - Call the following procedure with the ‘text’ element node. - - Procedure: resolve character - positioning: - - A recursive procedure that takes as input a node and - whose steps are as follows: -*/ - class CharacterPositioningResolver { - private int global = 0; - private boolean horizontal = true; - private boolean in_text_path = false; - private CharacterInformation[] result; - private String[] resolve_x; - private String[] resolve_y; - private String[] resolve_dx; - private String[] resolve_dy; - - private CharacterPositioningResolver( - CharacterInformation[] result, - String[] resolve_x, - String[] resolve_y, - String[] resolve_dx, - String[] resolve_dy - ) { - this.result = result; - this.resolve_x = resolve_x; - this.resolve_y = resolve_y; - this.resolve_dx = resolve_dx; - this.resolve_dy = resolve_dy; - } - - private void resolveCharacterPositioning(TextView node) { -/* - If node is a ‘text’ or ‘tspan’ node: -*/ - if (node.getClass() == TextView.class || node.getClass() == TSpanView.class) { -/* - Let index equal the "global index number" of the - first character in the node. -*/ - int index = global; -/* - Let x, y, dx, dy - and rotate be the lists of values from the - TODO corresponding attributes on node, or empty - lists if the corresponding attribute was not specified - or was invalid. -*/ - // https://www.w3.org/TR/SVG/text.html#TSpanElementXAttribute - String[] x = new String[]{}; - - // https://www.w3.org/TR/SVG/text.html#TSpanElementYAttribute - String[] y = new String[]{}; - - // Current SVGLengthList - // https://www.w3.org/TR/SVG/types.html#DataTypeLengths - - // https://www.w3.org/TR/SVG/text.html#TSpanElementDXAttribute - String[] dx = new String[]{}; - - // https://www.w3.org/TR/SVG/text.html#TSpanElementDYAttribute - String[] dy = new String[]{}; - - // Current SVGLengthList - // https://www.w3.org/TR/SVG/types.html#DataTypeNumbers - - // https://www.w3.org/TR/SVG/text.html#TSpanElementRotateAttribute - double[] rotate = new double[]{}; -/* - - If "in_text_path" flag is false: - Let new_chunk_count - = max(length of x, length of y). -*/ - int new_chunk_count; - if (!in_text_path) { - new_chunk_count = Math.max(x.length, y.length); -/* - - Else: -*/ - } else { -/* - If the "horizontal" flag is true: - - Let new_chunk_count = length of x. -*/ - if (horizontal) { - new_chunk_count = x.length; -/* - - Else: - - Let new_chunk_count = length of y. -*/ - } else { - new_chunk_count = y.length; - } - } -/* - - Let length be the number of DOM characters in the - subtree rooted at node. -*/ - String content = ((TSpanView) node).mContent; - int length = content == null ? 0 : content.length(); -/* - Let i = 0 and j = 0. - - i is an index of addressable characters in the node; - j is an index of all characters in the node. -*/ - int i = 0; - int j = 0; -/* - While j < length, do: -*/ - while (j < length) { -/* - This loop applies the ‘x’, ‘y’, - ‘dx’, ‘dy’ and ‘rotate’ - attributes to the content inside node. - If the "addressable" flag of result[index + - j] is true, then: -*/ - if (result[index + j].addressable) { -/* - If i < TODO new_check_count, then (typo) - set the "anchored chunk" flag of - result[index + j] to - true. Else set the flag to false. - - Setting the flag to false ensures that ‘x’ - and ‘y’ attributes set in a ‘text’ - element don't create anchored chunk in a ‘textPath’ - element when they should not. -*/ - result[index + j].anchoredChunk = i < new_chunk_count; -/* - - If i < length of x, - then set resolve_x[index - + j] to x[i]. -*/ - if (i < x.length) { - resolve_x[index + j] = x[i]; - } -/* - - If "in_text_path" flag is true and the "horizontal" - flag is false, unset - resolve_x[index]. - - The ‘x’ attribute is ignored for - vertical text on a path. -*/ - if (in_text_path && !horizontal) { - resolve_x[index] = ""; - } -/* - - If i < length of y, - then set resolve_y[index - + j] to y[i]. -*/ - if (i < y.length) { - resolve_y[index + j] = y[i]; - } -/* - If "in_text_path" flag is true and the "horizontal" - flag is true, unset - resolve_y[index]. - - The ‘y’ attribute is ignored for - horizontal text on a path. -*/ - if (in_text_path && horizontal) { - resolve_y[index] = ""; - } -/* - If i < length of dx, - then set resolve_dx[index - + j] to TODO dy[i]. (typo) -*/ - if (i < dx.length) { - resolve_dx[index + j] = dx[i]; - } -/* - If i < length of dy, - then set resolve_dy[index - + j] to dy[i]. -*/ - if (i < dy.length) { - resolve_dy[index + j] = dy[i]; - } -/* - If i < length of rotate, - then set the angle value of result[index - + j] to rotate[i]. - Otherwise, if rotate is not empty, then - set result[index + j] - to result[index + j − 1]. -*/ - if (i < rotate.length) { - result[index + j].rotate = rotate[i]; - } else if (rotate.length != 0) { - result[index + j].rotate = result[index + j - 1].rotate; - } -/* - Set i = i + 1. - Set j = j + 1. -*/ - } - i++; - j++; - } -/* - If node is a ‘textPath’ node: - - Let index equal the global index number of the - first character in the node (including descendant nodes). -*/ - } else if (node.getClass() == TextPathView.class) { - int index = global; -/* - Set the "anchored chunk" flag of result[index] - to true. - - A ‘textPath’ element always creates an anchored chunk. -*/ - result[index].anchoredChunk = true; -/* - Set in_text_path flag true. -*/ - in_text_path = true; -/* - For each child node child of node: - Resolve glyph - positioning of child. -*/ - for (int child = 0; child < node.getChildCount(); child++) { - resolveCharacterPositioning((TextView) node.getChildAt(child)); - } -/* - If node is a ‘textPath’ node: - - Set "in_text_path" flag false. - -*/ - if (node instanceof TextPathView) { - in_text_path = false; - } - } - } - } - - CharacterPositioningResolver resolver = new CharacterPositioningResolver( - result, - resolve_x, - resolve_y, - resolve_dx, - resolve_dy - ); -/* - Adjust positions: dx, dy - - The ‘dx’ and ‘dy’ adjustments are applied - before adjustments due to the ‘textLength’ attribute while - the ‘x’, ‘y’ and ‘rotate’ - adjustments are applied after. - - Let shift be the cumulative x and - y shifts due to ‘x’ and ‘y’ - attributes, initialized to (0,0). -*/ - PointF shift = new PointF(0, 0); -/* - For each array element with index i in result: -*/ - for (int i = 0; i < count; i++) { -/* - If resolve_x[i] is unspecified, set it to 0. - If resolve_y[i] is unspecified, set it to 0. -*/ - if (resolve_x[i].equals("")) { - resolve_x[i] = "0"; - } - if (resolve_y[i].equals("")) { - resolve_y[i] = "0"; - } -/* - Let shift.x = shift.x + resolve_x[i] - and shift.y = shift.y + resolve_y[i]. -*/ - shift.x = shift.x + Float.parseFloat(resolve_x[i]); - shift.y = shift.y + Float.parseFloat(resolve_y[i]); -/* - Let result[i].x = CSS_positions[i].x + shift.x - and result[i].y = CSS_positions[i].y + shift.y. -*/ - result[i].x = CSS_positions[i].x + shift.x; - result[i].y = CSS_positions[i].y + shift.y; - } -/* - TODO Apply ‘textLength’ attribute - - Set up: - - Define resolved descendant node as a - descendant of node with a valid ‘textLength’ - attribute that is not itself a descendant node of a - descendant node that has a valid ‘textLength’ - attribute. - - Call the following procedure with the ‘text’ element - node. - - Procedure: resolve text length: - - A recursive procedure that takes as input - a node and whose steps are as follows: - For each child node child of node: - - Resolve text length of child. - - Child nodes are adjusted before parent nodes. -*/ - class TextLengthResolver { - int global; - - private void resolveTextLength(TextView node) { + /* + If result is empty, then return result. + */ + if (count == 0) { + return result; + } + /* + + Let CSS_positions be an array of length + count whose entries will be filled with the + x and y positions of the corresponding + typographic character in root. The array + entries are initialized to (0, 0). + */ + PointF[] CSS_positions = new PointF[count]; + for (int i = 0; i < count; i++) { + CSS_positions[i] = new PointF(0, 0); + } + /* + Let "horizontal" be a flag, true if the writing mode of ‘text’ + is horizontal, false otherwise. + */ + final boolean horizontal = true; + /* + Set flags and assign initial positions + + For each array element with index i in + result: + */ + for (int i = 0; i < count; i++) { + /* + TODO Set addressable to false if the character at index i was: + + part of the text content of a non-rendered element + + discarded during layout due to being a + collapsed + white space character, a soft hyphen character, or a + bidi control character; or + + + discarded during layout due to being a + collapsed + segment break; or + + + trimmed + from the start or end of a line. + + Since there is collapsible white space not addressable by glyph + positioning attributes in the following ‘text’ element + (with a standard font), the "B" glyph will be placed at x=300. + + + A + B + + + This is because the white space before the "A", and all but one white space + character between the "A" and "B", is collapsed away or trimmed. + + */ + result[i].addressable = true; + /* + + Set middle to true if the character at index i + TODO is the second or later character that corresponds to a typographic character. + */ + result[i].middle = false; + /* + + TODO If the character at index i corresponds to a typographic character at the beginning of a line, then set the "anchored + chunk" flag of result[i] to true. + + This ensures chunks shifted by text-anchor do not + span multiple lines. + */ + result[i].anchoredChunk = i == 0; + /* + + If addressable is true and middle is false then + set CSS_positions[i] to the position of the + TODO corresponding typographic character as determined by the CSS + renderer. Otherwise, if i > 0, then set + CSS_positions[i] = + CSS_positions[i − 1] + + */ + if (result[i].addressable && !result[i].middle) { + CSS_positions[i].set(0, 0); + } else if (i > 0) { + CSS_positions[i].set(CSS_positions[i - 1]); + } + } + /* + + Resolve character positioning + + Position adjustments (e.g values in a ‘x’ attribute) + specified by a node apply to all characters in that node including + characters in the node's descendants. Adjustments specified in + descendant nodes, however, override adjustments from ancestor + nodes. This section resolves which adjustments are to be applied to + which characters. It also directly sets the rotate coordinate + of result. + + Set up: + + Let resolve_x, resolve_y, + resolve_dx, and resolve_dy be arrays of + length count whose entries are all initialized + to "unspecified". + */ + String[] resolve_x = new String[count]; + String[] resolve_y = new String[count]; + String[] resolve_dx = new String[count]; + String[] resolve_dy = new String[count]; + /* + + Set "in_text_path" flag false. + + This flag will allow ‘y’ (‘x’) + attribute values to be ignored for horizontal (vertical) + text inside ‘textPath’ elements. + */ + boolean in_text_path = false; + /* + Call the following procedure with the ‘text’ element node. + + Procedure: resolve character + positioning: + + A recursive procedure that takes as input a node and + whose steps are as follows: + */ + class CharacterPositioningResolver { + private int global = 0; + private boolean horizontal = true; + private boolean in_text_path = false; + private CharacterInformation[] result; + private String[] resolve_x; + private String[] resolve_y; + private String[] resolve_dx; + private String[] resolve_dy; + + private CharacterPositioningResolver( + CharacterInformation[] result, + String[] resolve_x, + String[] resolve_y, + String[] resolve_dx, + String[] resolve_dy) { + this.result = result; + this.resolve_x = resolve_x; + this.resolve_y = resolve_y; + this.resolve_dx = resolve_dx; + this.resolve_dy = resolve_dy; + } + + private void resolveCharacterPositioning(TextView node) { + /* + If node is a ‘text’ or ‘tspan’ node: + */ + if (node.getClass() == TextView.class || node.getClass() == TSpanView.class) { + /* + Let index equal the "global index number" of the + first character in the node. + */ + int index = global; + /* + Let x, y, dx, dy + and rotate be the lists of values from the + TODO corresponding attributes on node, or empty + lists if the corresponding attribute was not specified + or was invalid. + */ + // https://www.w3.org/TR/SVG/text.html#TSpanElementXAttribute + String[] x = new String[] {}; + + // https://www.w3.org/TR/SVG/text.html#TSpanElementYAttribute + String[] y = new String[] {}; + + // Current SVGLengthList + // https://www.w3.org/TR/SVG/types.html#DataTypeLengths + + // https://www.w3.org/TR/SVG/text.html#TSpanElementDXAttribute + String[] dx = new String[] {}; + + // https://www.w3.org/TR/SVG/text.html#TSpanElementDYAttribute + String[] dy = new String[] {}; + + // Current SVGLengthList + // https://www.w3.org/TR/SVG/types.html#DataTypeNumbers + + // https://www.w3.org/TR/SVG/text.html#TSpanElementRotateAttribute + double[] rotate = new double[] {}; + /* + + If "in_text_path" flag is false: + Let new_chunk_count + = max(length of x, length of y). + */ + int new_chunk_count; + if (!in_text_path) { + new_chunk_count = Math.max(x.length, y.length); /* - If node is a ‘text’ or ‘tspan’ node - and if the node has a valid ‘textLength’ attribute value: -*/ - final Class nodeClass = node.getClass(); - final boolean validTextLength = node.mTextLength != null; - if ( - (nodeClass == TSpanView.class) - && validTextLength - ) { - /* - Let a = +∞ and b = −∞. -*/ - double a = Double.POSITIVE_INFINITY; - double b = Double.NEGATIVE_INFINITY; -/* + Else: + */ + } else { + /* + If the "horizontal" flag is true: + Let new_chunk_count = length of x. + */ + if (horizontal) { + new_chunk_count = x.length; + /* - Let i and j be the global - index of the first character and last characters - in node, respectively. -*/ - String content = ((TSpanView) node).mContent; - int i = global; - int j = i + (content == null ? 0 : content.length()); -/* - For each index k in the range - [i, j] where the "addressable" flag - of result[k] is true: + Else: - This loop finds the left-(top-) most and - right-(bottom-) most extents of the typographic characters within the node and checks for - forced line breaks. -*/ - for (int k = i; k <= j; k++) { - if (!result[i].addressable) { - continue; - } -/* - If the character at k is a linefeed - or carriage return, return. No adjustments due to - ‘textLength’ are made to a node with - a forced line break. -*/ - switch (result[i].character) { - case '\n': - case '\r': - return; - } -/* - Let pos = the x coordinate of the position - in result[k], if the "horizontal" - flag is true, and the y coordinate otherwise. -*/ - double pos = horizontal ? result[k].x : result[k].y; -/* - Let advance = the advance of - the typographic character corresponding to - character k. [NOTE: This advance will be - negative for RTL horizontal text.] -*/ - double advance = result[k].advance; -/* - Set a = - min(a, pos, pos - + advance). + Let new_chunk_count = length of y. + */ + } else { + new_chunk_count = y.length; + } + } + /* + Let length be the number of DOM characters in the + subtree rooted at node. + */ + String content = ((TSpanView) node).mContent; + int length = content == null ? 0 : content.length(); + /* + Let i = 0 and j = 0. - Set b = - max(b, pos, pos - + advance). -*/ - a = Math.min(a, Math.min(pos, pos + advance)); - b = Math.max(b, Math.max(pos, pos + advance)); - } -/* + i is an index of addressable characters in the node; + j is an index of all characters in the node. + */ + int i = 0; + int j = 0; + /* + While j < length, do: + */ + while (j < length) { + /* + This loop applies the ‘x’, ‘y’, + ‘dx’, ‘dy’ and ‘rotate’ + attributes to the content inside node. + If the "addressable" flag of result[index + + j] is true, then: + */ + if (result[index + j].addressable) { + /* + If i < TODO new_check_count, then (typo) + set the "anchored chunk" flag of + result[index + j] to + true. Else set the flag to false. - If a ≠ +∞ then: + Setting the flag to false ensures that ‘x’ + and ‘y’ attributes set in a ‘text’ + element don't create anchored chunk in a ‘textPath’ + element when they should not. + */ + result[index + j].anchoredChunk = i < new_chunk_count; + /* -*/ - if (a != Double.POSITIVE_INFINITY) { -/* + If i < length of x, + then set resolve_x[index + + j] to x[i]. + */ + if (i < x.length) { + resolve_x[index + j] = x[i]; + } + /* - Find the distance delta = ‘textLength’ - computed value − (b − a). -*/ - double delta = node.mTextLength.value - (b - a); -/* + If "in_text_path" flag is true and the "horizontal" + flag is false, unset + resolve_x[index]. - User agents are required to shift the last - typographic character in the node by - delta, in the positive x direction - if the "horizontal" flag is true and if - direction is - lrt, in the - negative x direction if the "horizontal" flag - is true and direction is - rtl, or in the - positive y direction otherwise. User agents - are free to adjust intermediate - typographic characters for optimal - typography. The next steps indicate one way to - adjust typographic characters when - the value of ‘lengthAdjust’ is - spacing. + The ‘x’ attribute is ignored for + vertical text on a path. + */ + if (in_text_path && !horizontal) { + resolve_x[index] = ""; + } + /* - Find n, the total number of - typographic characters in this node - TODO including any descendant nodes that are not resolved - descendant nodes or within a resolved descendant + If i < length of y, + then set resolve_y[index + + j] to y[i]. + */ + if (i < y.length) { + resolve_y[index + j] = y[i]; + } + /* + If "in_text_path" flag is true and the "horizontal" + flag is true, unset + resolve_y[index]. + + The ‘y’ attribute is ignored for + horizontal text on a path. + */ + if (in_text_path && horizontal) { + resolve_y[index] = ""; + } + /* + If i < length of dx, + then set resolve_dx[index + + j] to TODO dy[i]. (typo) + */ + if (i < dx.length) { + resolve_dx[index + j] = dx[i]; + } + /* + If i < length of dy, + then set resolve_dy[index + + j] to dy[i]. + */ + if (i < dy.length) { + resolve_dy[index + j] = dy[i]; + } + /* + If i < length of rotate, + then set the angle value of result[index + + j] to rotate[i]. + Otherwise, if rotate is not empty, then + set result[index + j] + to result[index + j − 1]. + */ + if (i < rotate.length) { + result[index + j].rotate = rotate[i]; + } else if (rotate.length != 0) { + result[index + j].rotate = result[index + j - 1].rotate; + } + /* + Set i = i + 1. + Set j = j + 1. + */ + } + i++; + j++; + } + /* + If node is a ‘textPath’ node: + + Let index equal the global index number of the + first character in the node (including descendant nodes). + */ + } else if (node.getClass() == TextPathView.class) { + int index = global; + /* + Set the "anchored chunk" flag of result[index] + to true. + + A ‘textPath’ element always creates an anchored chunk. + */ + result[index].anchoredChunk = true; + /* + Set in_text_path flag true. + */ + in_text_path = true; + /* + For each child node child of node: + Resolve glyph + positioning of child. + */ + for (int child = 0; child < node.getChildCount(); child++) { + resolveCharacterPositioning((TextView) node.getChildAt(child)); + } + /* + If node is a ‘textPath’ node: + + Set "in_text_path" flag false. + + */ + if (node instanceof TextPathView) { + in_text_path = false; + } + } + } + } + + CharacterPositioningResolver resolver = + new CharacterPositioningResolver(result, resolve_x, resolve_y, resolve_dx, resolve_dy); + /* + Adjust positions: dx, dy + + The ‘dx’ and ‘dy’ adjustments are applied + before adjustments due to the ‘textLength’ attribute while + the ‘x’, ‘y’ and ‘rotate’ + adjustments are applied after. + + Let shift be the cumulative x and + y shifts due to ‘x’ and ‘y’ + attributes, initialized to (0,0). + */ + PointF shift = new PointF(0, 0); + /* + For each array element with index i in result: + */ + for (int i = 0; i < count; i++) { + /* + If resolve_x[i] is unspecified, set it to 0. + If resolve_y[i] is unspecified, set it to 0. + */ + if (resolve_x[i].equals("")) { + resolve_x[i] = "0"; + } + if (resolve_y[i].equals("")) { + resolve_y[i] = "0"; + } + /* + Let shift.x = shift.x + resolve_x[i] + and shift.y = shift.y + resolve_y[i]. + */ + shift.x = shift.x + Float.parseFloat(resolve_x[i]); + shift.y = shift.y + Float.parseFloat(resolve_y[i]); + /* + Let result[i].x = CSS_positions[i].x + shift.x + and result[i].y = CSS_positions[i].y + shift.y. + */ + result[i].x = CSS_positions[i].x + shift.x; + result[i].y = CSS_positions[i].y + shift.y; + } + /* + TODO Apply ‘textLength’ attribute + + Set up: + + Define resolved descendant node as a + descendant of node with a valid ‘textLength’ + attribute that is not itself a descendant node of a + descendant node that has a valid ‘textLength’ + attribute. + + Call the following procedure with the ‘text’ element node. -*/ - int n = 0; - int resolvedDescendantNodes = 0; - for (int c = 0; c < node.getChildCount(); c++) { - if (((TextPathView) node.getChildAt(c)).mTextLength == null) { - String ccontent = ((TSpanView) node).mContent; - n += ccontent == null ? 0 : ccontent.length(); - } else { - result[n].firstCharacterInResolvedDescendant = true; - resolvedDescendantNodes++; - } - } -/* - Let n = n + number of - resolved descendant nodes − 1. -*/ - n += resolvedDescendantNodes - 1; -/* - Each resolved descendant node is treated as if it - were a single - typographic character in this - context. - Find the per-character adjustment δ - = delta/n. + Procedure: resolve text length: - Let shift = 0. -*/ - double perCharacterAdjustment = delta / n; - double shift = 0; -/* - For each index k in the range [i,j]: -*/ - for (int k = i; k <= j; k++) { -/* - Add shift to the x coordinate of the - position in result[k], if the "horizontal" - flag is true, and to the y coordinate - otherwise. -*/ - if (horizontal) { - result[k].x += shift; - } else { - result[k].y += shift; - } -/* + A recursive procedure that takes as input + a node and whose steps are as follows: + For each child node child of node: + + Resolve text length of child. + + Child nodes are adjusted before parent nodes. + */ + class TextLengthResolver { + int global; + + private void resolveTextLength(TextView node) { + /* + + If node is a ‘text’ or ‘tspan’ node + and if the node has a valid ‘textLength’ attribute value: + */ + final Class nodeClass = node.getClass(); + final boolean validTextLength = node.mTextLength != null; + if ((nodeClass == TSpanView.class) && validTextLength) { + /* + Let a = +∞ and b = −∞. + */ + double a = Double.POSITIVE_INFINITY; + double b = Double.NEGATIVE_INFINITY; + /* + + + Let i and j be the global + index of the first character and last characters + in node, respectively. + */ + String content = ((TSpanView) node).mContent; + int i = global; + int j = i + (content == null ? 0 : content.length()); + /* + For each index k in the range + [i, j] where the "addressable" flag + of result[k] is true: + + This loop finds the left-(top-) most and + right-(bottom-) most extents of the typographic characters within the node and checks for + forced line breaks. + */ + for (int k = i; k <= j; k++) { + if (!result[i].addressable) { + continue; + } + /* + If the character at k is a linefeed + or carriage return, return. No adjustments due to + ‘textLength’ are made to a node with + a forced line break. + */ + switch (result[i].character) { + case '\n': + case '\r': + return; + } + /* + Let pos = the x coordinate of the position + in result[k], if the "horizontal" + flag is true, and the y coordinate otherwise. + */ + double pos = horizontal ? result[k].x : result[k].y; + /* + Let advance = the advance of + the typographic character corresponding to + character k. [NOTE: This advance will be + negative for RTL horizontal text.] + */ + double advance = result[k].advance; + /* + Set a = + min(a, pos, pos + + advance). + + + Set b = + max(b, pos, pos + + advance). + */ + a = Math.min(a, Math.min(pos, pos + advance)); + b = Math.max(b, Math.max(pos, pos + advance)); + } + /* + + If a ≠ +∞ then: + + */ + if (a != Double.POSITIVE_INFINITY) { + /* + + Find the distance delta = ‘textLength’ + computed value − (b − a). + */ + double delta = node.mTextLength.value - (b - a); + /* + + User agents are required to shift the last + typographic character in the node by + delta, in the positive x direction + if the "horizontal" flag is true and if + direction is + lrt, in the + negative x direction if the "horizontal" flag + is true and direction is + rtl, or in the + positive y direction otherwise. User agents + are free to adjust intermediate + typographic characters for optimal + typography. The next steps indicate one way to + adjust typographic characters when + the value of ‘lengthAdjust’ is + spacing. + + Find n, the total number of + typographic characters in this node + TODO including any descendant nodes that are not resolved + descendant nodes or within a resolved descendant + node. + */ + int n = 0; + int resolvedDescendantNodes = 0; + for (int c = 0; c < node.getChildCount(); c++) { + if (((TextPathView) node.getChildAt(c)).mTextLength == null) { + String ccontent = ((TSpanView) node).mContent; + n += ccontent == null ? 0 : ccontent.length(); + } else { + result[n].firstCharacterInResolvedDescendant = true; + resolvedDescendantNodes++; + } + } + /* + Let n = n + number of + resolved descendant nodes − 1. + */ + n += resolvedDescendantNodes - 1; + /* + Each resolved descendant node is treated as if it + were a single + typographic character in this + context. + + Find the per-character adjustment δ + = delta/n. + + Let shift = 0. + */ + double perCharacterAdjustment = delta / n; + double shift = 0; + /* + For each index k in the range [i,j]: + */ + for (int k = i; k <= j; k++) { + /* + Add shift to the x coordinate of the + position in result[k], if the "horizontal" + flag is true, and to the y coordinate + otherwise. + */ + if (horizontal) { + result[k].x += shift; + } else { + result[k].y += shift; + } + /* If the "middle" flag for result[k] is not true and k is not a character in a resolved descendant node other than the first character then shift = shift + δ. */ - if (!result[k].middle && (!result[k].resolved || result[k].firstCharacterInResolvedDescendant)) { - shift += perCharacterAdjustment; - } - } - } - } + if (!result[k].middle + && (!result[k].resolved || result[k].firstCharacterInResolvedDescendant)) { + shift += perCharacterAdjustment; + } } + } } - TextLengthResolver lengthResolver = new TextLengthResolver(); - lengthResolver.resolveTextLength(text); -/* - - Adjust positions: x, y - - This loop applies ‘x’ and ‘y’ values, - and ensures that text-anchor chunks do not start in - the middle of a typographic character. - - Let shift be the current adjustment due to - the ‘x’ and ‘y’ attributes, - initialized to (0,0). - - Set index = 1. -*/ - shift.set(0, 0); - int index = 1; -/* - While index < count: -*/ - while (index < count) { -/* - TODO If resolved_x[index] is set, then let (typo) - shift.x = - resolved_x[index] − - result.x[index]. -*/ - if (resolve_x[index] != null) { - shift.x = (float) (Double.parseDouble(resolve_x[index]) - result[index].x); - } -/* - TODO If resolved_y[index] is set, then let (typo) - shift.y = - resolved_y[index] − - result.y[index]. -*/ - if (resolve_y[index] != null) { - shift.y = (float) (Double.parseDouble(resolve_y[index]) - result[index].y); - } -/* - Let result.x[index] = - result.x[index] + shift.x - and result.y[index] = - result.y[index] + shift.y. -*/ - result[index].x += shift.x; - result[index].y += shift.y; -/* - If the "middle" and "anchored chunk" flags - of result[index] are both true, then: -*/ - if (result[index].middle && result[index].anchoredChunk) { -/* - Set the "anchored chunk" flag - of result[index] to false. -*/ - result[index].anchoredChunk = false; - } -/* - - If index + 1 < count, then set - the "anchored chunk" flag - of result[index + 1] to true. -*/ - if (index + 1 < count) { - result[index + 1].anchoredChunk = true; - } -/* - Set index to index + 1. -*/ - index++; - } -/* - - Apply anchoring - - TODO For each slice result[i..j] - (inclusive of both i and j), where: - - the "anchored chunk" flag of result[i] - is true, - - the "anchored chunk" flags - of result[k] where i - < k ≤ j are false, and - - j = count − 1 or the "anchored - chunk" flag of result[j + 1] is - true; - do: - - This loops over each anchored chunk. - - Let a = +∞ and b = −∞. - - For each index k in the range - [i, j] where the "addressable" flag - of result[k] is true: - - This loop finds the left-(top-) most and - right-(bottom-) most extents of the typographic character within the anchored chunk. -*/ - int i = 0; - double a = Double.POSITIVE_INFINITY; - double b = Double.NEGATIVE_INFINITY; - double prevA = Double.POSITIVE_INFINITY; - double prevB = Double.NEGATIVE_INFINITY; - for (int k = 0; k < count; k++) { - if (!result[k].addressable) { - continue; - } - if (result[k].anchoredChunk) { - prevA = a; - prevB = b; - a = Double.POSITIVE_INFINITY; - b = Double.NEGATIVE_INFINITY; - } -/* - Let pos = the x coordinate of the position - in result[k], if the "horizontal" flag - is true, and the y coordinate otherwise. - - Let advance = the advance of - the typographic character corresponding to - character k. [NOTE: This advance will be - negative for RTL horizontal text.] - - Set a = - min(a, pos, pos - + advance). - - Set b = - max(b, pos, pos - + advance). -*/ - double pos = horizontal ? result[k].x : result[k].y; - double advance = result[k].advance; - a = Math.min(a, Math.min(pos, pos + advance)); - b = Math.max(b, Math.max(pos, pos + advance)); -/* - If a ≠ +∞, then: - - Here we perform the text anchoring. - - Let shift be the x coordinate of - result[i], if the "horizontal" flag - is true, and the y coordinate otherwise. - - TODO Adjust shift based on the value of text-anchor - TODO and direction of the element the character at - index i is in: - - (start, ltr) or (end, rtl) - Set shift = shift − a. - (start, rtl) or (end, ltr) - Set shift = shift − b. - (middle, ltr) or (middle, rtl) - Set shift = shift − (a + b) / 2. -*/ - if ((k > 0 && result[k].anchoredChunk && prevA != Double.POSITIVE_INFINITY) || k == count - 1) { - TextAnchor anchor = TextAnchor.start; - Direction direction = Direction.ltr; - - if (k == count - 1) { - prevA = a; - prevB = b; - } - - double anchorShift = horizontal ? result[i].x : result[i].y; - switch (anchor) { - case start: - if (direction == Direction.ltr) { - anchorShift = anchorShift - prevA; - } else { - anchorShift = anchorShift - prevB; - } - break; - - case middle: - if (direction == Direction.ltr) { - anchorShift = anchorShift - (prevA + prevB) / 2; - } else { - anchorShift = anchorShift - (prevA + prevB) / 2; - } - break; - - case end: - if (direction == Direction.ltr) { - anchorShift = anchorShift - prevB; - } else { - anchorShift = anchorShift - prevA; - } - break; - } -/* - For each index k in the range [i, j]: - - Add shift to the x coordinate of the position - in result[k], if the "horizontal" - flag is true, and to the y coordinate otherwise. -*/ - int j = k == count - 1 ? k : k - 1; - for (int r = i; r <= j; r++) { - if (horizontal) { - result[r].x += anchorShift; - } else { - result[r].y += anchorShift; - } - } - - i = k; - } - } -/* - - Position on path - - Set index = 0. - - Set the "in path" flag to false. - - Set the "after path" flag to false. - - Let path_end be an offset for characters that follow - a ‘textPath’ element. Set path_end to (0,0). - - While index < count: -*/ - index = 0; - boolean inPath = false; - boolean afterPath = false; - PointF path_end = new PointF(0, 0); - Path textPath = null; - PathMeasure pm = new PathMeasure(); - while (index < count) { -/* - If the character at index i is within a - ‘textPath’ element and corresponds to a typographic character, then: - - Set "in path" flag to true. -*/ - final TextPathView textPathView = inTextPath.get(index); - if (textPathView != null && result[index].addressable) { - textPath = textPathView.getTextPath(null, null); - inPath = true; -/* - - If the "middle" flag of - result[index] is false, then: -*/ - if (!result[index].middle) { -/* - Here we apply ‘textPath’ positioning. - - Let path be the equivalent path of - the basic shape element referenced by - the ‘textPath’ element, or an empty path if - the reference is invalid. - - If the ‘side’ attribute of - the ‘textPath’ element is - 'right', then - TODO reverse path. -*/ - Path path = textPath; - if (textPathView.getSide() == TextPathSide.right) { - - } -/* - Let length be the length - of path. -*/ - pm.setPath(path, false); - double length = pm.getLength(); -/* - Let offset be the value of the - ‘textPath’ element's - ‘startOffset’ attribute, adjusted - due to any ‘pathLength’ attribute on the - referenced element (if the referenced element is - a ‘path’ element). -*/ - double offset = textPathView.getStartOffset().value; -/* - Let advance = the advance of - the typographic character corresponding - to character TODO k. (typo) [NOTE: This advance will - be negative for RTL horizontal text.] -*/ - double advance = result[index].advance; -/* - Let (x, y) - and angle be the position and angle - in result[index]. -*/ - double x = result[index].x; - double y = result[index].y; - double angle = result[index].rotate; -/* - - Let mid be a coordinate value depending - on the value of the "horizontal" flag: - - true - mid is x + advance / 2 - + offset - false - mid is y + advance / 2 - + offset -*/ - double mid = (horizontal ? x : y) + advance / 2 + offset; -/* - - The user agent is free to make any additional adjustments to - mid necessary to ensure high quality typesetting - TODO due to a ‘spacing’ value of - 'auto' or a - ‘method’ value of - 'stretch'. - - If path is not a closed subpath and - mid < 0 or mid > length, - set the "hidden" flag of result[index] to true. -*/ - if (!pm.isClosed() && (mid < 0 || mid > length)) { - result[index].hidden = true; - } -/* - If path is a closed subpath depending on - the values of text-anchor and direction of - the element the character at index is in: -*/ - if (pm.isClosed()) { -/* - This implements the special wrapping criteria for single - closed subpaths. - - (start, ltr) or (end, rtl) - - If mid−offset < 0 - or mid−offset > length, - set the "hidden" flag of result[index] to true. - - (middle, ltr) or (middle, rtl) - - If - If mid−offset < −length/2 - or mid−offset > length/2, - set the "hidden" flag of result[index] to true. - - (start, rtl) or (end, ltr) - - If mid−offset < −length - or mid−offset > 0, - set the "hidden" flag of result[index] to true. -*/ - TextAnchor anchor = TextAnchor.start; - Direction direction = Direction.ltr; - - double anchorShift = horizontal ? result[i].x : result[i].y; - switch (anchor) { - case start: - if (direction == Direction.ltr) { - if (mid < 0 || mid > length) { - result[index].hidden = true; - } - } else { - if (mid < -length || mid > 0) { - result[index].hidden = true; - } - } - break; - - case middle: - if (mid < -length / 2 || mid > length / 2) { - result[index].hidden = true; - } - break; - - case end: - if (direction == Direction.ltr) { - if (mid < -length || mid > 0) { - result[index].hidden = true; - } - } else { - if (mid < 0 || mid > length) { - result[index].hidden = true; - } - } - break; - } - } -/* - Set mid = mid mod length. -*/ - mid %= length; -/* - If the hidden flag is false: -*/ - if (!result[index].hidden) { -/* - Let point be the position and - t be the unit vector tangent to - the point mid distance - along path. -*/ - float[] point = new float[2]; - float[] t = new float[2]; - pm.getPosTan((float) mid, point, t); - final double tau = 2 * Math.PI; - final double radToDeg = 360 / tau; - final double r = Math.atan2(t[1], t[0]) * radToDeg; -/* - If the "horizontal" flag is -*/ - if (horizontal) { -/* - true - - Let n be the normal unit vector - pointing in the direction t + 90°. -*/ - double normAngle = r + 90; - double[] n = new double[]{Math.cos(normAngle), Math.sin(normAngle)}; -/* - Let o be the horizontal distance from the - TODO vertical center line of the glyph to the alignment point. -*/ - double o = 0; -/* - Then set the position in - result[index] to - point - - o×t + - y×n. - - Let r be the angle from - the positive x-axis to the tangent. - - Set the angle value - in result[index] - to angle + r. -*/ - result[index].rotate += r; - } else { -/* - false - - Let n be the normal unit vector - pointing in the direction t - 90°. -*/ - double normAngle = r - 90; - double[] n = new double[]{Math.cos(normAngle), Math.sin(normAngle)}; -/* - Let o be the vertical distance from the - TODO horizontal center line of the glyph to the alignment point. -*/ - double o = 0; -/* - - Then set the position in - result[index] to - point - - o×t + - x×n. - - Let r be the angle from - the positive y-axis to the tangent. - - Set the angle value - in result[index] - to angle + r. -*/ - result[index].rotate += r; - } - } -/* - - Otherwise, the "middle" flag - of result[index] is true: - - Set the position and angle values - of result[index] to those - in result[index − 1]. -*/ - } else { - result[index].x = result[index - 1].x; - result[index].y = result[index - 1].y; - result[index].rotate = result[index - 1].rotate; - } - - } -/* - If the character at index i is not within a - ‘textPath’ element and corresponds to a typographic character, then: - - This sets the starting point for rendering any characters that - occur after a ‘textPath’ element to the end of the path. -*/ - if (textPathView == null && result[index].addressable) { -/* - If the "in path" flag is true: - - Set the "in path" flag to false. - - Set the "after path" flag to true. - - Set path_end equal to the end point of the path - referenced by ‘textPath’ − the position of - result[index]. -*/ - if (inPath) { - inPath = false; - afterPath = true; - pm.setPath(textPath, false); - float[] pos = new float[2]; - pm.getPosTan(pm.getLength(), pos, null); - path_end.set(pos[0], pos[1]); - } -/* - - If the "after path" is true. - - If anchored chunk of - result[index] is true, set the - "after path" flag to false. - - Else, - let result.x[index] = - result.x[index] + path_end.x - and result.y[index] = - result.y[index] + path_end.y. -*/ - if (afterPath) { - if (result[index].anchoredChunk) { - afterPath = false; - } else { - result[index].x += path_end.x; - result[index].y += path_end.y; - } - } - } -/* - - Set index = index + 1. -*/ - index++; - } -/* - Return result -*/ - return result; + } } + TextLengthResolver lengthResolver = new TextLengthResolver(); + lengthResolver.resolveTextLength(text); + /* + + Adjust positions: x, y + + This loop applies ‘x’ and ‘y’ values, + and ensures that text-anchor chunks do not start in + the middle of a typographic character. + + Let shift be the current adjustment due to + the ‘x’ and ‘y’ attributes, + initialized to (0,0). + + Set index = 1. + */ + shift.set(0, 0); + int index = 1; + /* + While index < count: + */ + while (index < count) { + /* + TODO If resolved_x[index] is set, then let (typo) + shift.x = + resolved_x[index] − + result.x[index]. + */ + if (resolve_x[index] != null) { + shift.x = (float) (Double.parseDouble(resolve_x[index]) - result[index].x); + } + /* + TODO If resolved_y[index] is set, then let (typo) + shift.y = + resolved_y[index] − + result.y[index]. + */ + if (resolve_y[index] != null) { + shift.y = (float) (Double.parseDouble(resolve_y[index]) - result[index].y); + } + /* + Let result.x[index] = + result.x[index] + shift.x + and result.y[index] = + result.y[index] + shift.y. + */ + result[index].x += shift.x; + result[index].y += shift.y; + /* + If the "middle" and "anchored chunk" flags + of result[index] are both true, then: + */ + if (result[index].middle && result[index].anchoredChunk) { + /* + Set the "anchored chunk" flag + of result[index] to false. + */ + result[index].anchoredChunk = false; + } + /* + + If index + 1 < count, then set + the "anchored chunk" flag + of result[index + 1] to true. + */ + if (index + 1 < count) { + result[index + 1].anchoredChunk = true; + } + /* + Set index to index + 1. + */ + index++; + } + /* + + Apply anchoring + + TODO For each slice result[i..j] + (inclusive of both i and j), where: + + the "anchored chunk" flag of result[i] + is true, + + the "anchored chunk" flags + of result[k] where i + < k ≤ j are false, and + + j = count − 1 or the "anchored + chunk" flag of result[j + 1] is + true; + do: + + This loops over each anchored chunk. + + Let a = +∞ and b = −∞. + + For each index k in the range + [i, j] where the "addressable" flag + of result[k] is true: + + This loop finds the left-(top-) most and + right-(bottom-) most extents of the typographic character within the anchored chunk. + */ + int i = 0; + double a = Double.POSITIVE_INFINITY; + double b = Double.NEGATIVE_INFINITY; + double prevA = Double.POSITIVE_INFINITY; + double prevB = Double.NEGATIVE_INFINITY; + for (int k = 0; k < count; k++) { + if (!result[k].addressable) { + continue; + } + if (result[k].anchoredChunk) { + prevA = a; + prevB = b; + a = Double.POSITIVE_INFINITY; + b = Double.NEGATIVE_INFINITY; + } + /* + Let pos = the x coordinate of the position + in result[k], if the "horizontal" flag + is true, and the y coordinate otherwise. + + Let advance = the advance of + the typographic character corresponding to + character k. [NOTE: This advance will be + negative for RTL horizontal text.] + + Set a = + min(a, pos, pos + + advance). + + Set b = + max(b, pos, pos + + advance). + */ + double pos = horizontal ? result[k].x : result[k].y; + double advance = result[k].advance; + a = Math.min(a, Math.min(pos, pos + advance)); + b = Math.max(b, Math.max(pos, pos + advance)); + /* + If a ≠ +∞, then: + + Here we perform the text anchoring. + + Let shift be the x coordinate of + result[i], if the "horizontal" flag + is true, and the y coordinate otherwise. + + TODO Adjust shift based on the value of text-anchor + TODO and direction of the element the character at + index i is in: + + (start, ltr) or (end, rtl) + Set shift = shift − a. + (start, rtl) or (end, ltr) + Set shift = shift − b. + (middle, ltr) or (middle, rtl) + Set shift = shift − (a + b) / 2. + */ + if ((k > 0 && result[k].anchoredChunk && prevA != Double.POSITIVE_INFINITY) + || k == count - 1) { + TextAnchor anchor = TextAnchor.start; + Direction direction = Direction.ltr; + + if (k == count - 1) { + prevA = a; + prevB = b; + } + + double anchorShift = horizontal ? result[i].x : result[i].y; + switch (anchor) { + case start: + if (direction == Direction.ltr) { + anchorShift = anchorShift - prevA; + } else { + anchorShift = anchorShift - prevB; + } + break; + + case middle: + if (direction == Direction.ltr) { + anchorShift = anchorShift - (prevA + prevB) / 2; + } else { + anchorShift = anchorShift - (prevA + prevB) / 2; + } + break; + + case end: + if (direction == Direction.ltr) { + anchorShift = anchorShift - prevB; + } else { + anchorShift = anchorShift - prevA; + } + break; + } + /* + For each index k in the range [i, j]: + + Add shift to the x coordinate of the position + in result[k], if the "horizontal" + flag is true, and to the y coordinate otherwise. + */ + int j = k == count - 1 ? k : k - 1; + for (int r = i; r <= j; r++) { + if (horizontal) { + result[r].x += anchorShift; + } else { + result[r].y += anchorShift; + } + } + + i = k; + } + } + /* + + Position on path + + Set index = 0. + + Set the "in path" flag to false. + + Set the "after path" flag to false. + + Let path_end be an offset for characters that follow + a ‘textPath’ element. Set path_end to (0,0). + + While index < count: + */ + index = 0; + boolean inPath = false; + boolean afterPath = false; + PointF path_end = new PointF(0, 0); + Path textPath = null; + PathMeasure pm = new PathMeasure(); + while (index < count) { + /* + If the character at index i is within a + ‘textPath’ element and corresponds to a typographic character, then: + + Set "in path" flag to true. + */ + final TextPathView textPathView = inTextPath.get(index); + if (textPathView != null && result[index].addressable) { + textPath = textPathView.getTextPath(null, null); + inPath = true; + /* + + If the "middle" flag of + result[index] is false, then: + */ + if (!result[index].middle) { + /* + Here we apply ‘textPath’ positioning. + + Let path be the equivalent path of + the basic shape element referenced by + the ‘textPath’ element, or an empty path if + the reference is invalid. + + If the ‘side’ attribute of + the ‘textPath’ element is + 'right', then + TODO reverse path. + */ + Path path = textPath; + if (textPathView.getSide() == TextPathSide.right) {} + + /* + Let length be the length + of path. + */ + pm.setPath(path, false); + double length = pm.getLength(); + /* + Let offset be the value of the + ‘textPath’ element's + ‘startOffset’ attribute, adjusted + due to any ‘pathLength’ attribute on the + referenced element (if the referenced element is + a ‘path’ element). + */ + double offset = textPathView.getStartOffset().value; + /* + Let advance = the advance of + the typographic character corresponding + to character TODO k. (typo) [NOTE: This advance will + be negative for RTL horizontal text.] + */ + double advance = result[index].advance; + /* + Let (x, y) + and angle be the position and angle + in result[index]. + */ + double x = result[index].x; + double y = result[index].y; + double angle = result[index].rotate; + /* + + Let mid be a coordinate value depending + on the value of the "horizontal" flag: + + true + mid is x + advance / 2 + + offset + false + mid is y + advance / 2 + + offset + */ + double mid = (horizontal ? x : y) + advance / 2 + offset; + /* + + The user agent is free to make any additional adjustments to + mid necessary to ensure high quality typesetting + TODO due to a ‘spacing’ value of + 'auto' or a + ‘method’ value of + 'stretch'. + + If path is not a closed subpath and + mid < 0 or mid > length, + set the "hidden" flag of result[index] to true. + */ + if (!pm.isClosed() && (mid < 0 || mid > length)) { + result[index].hidden = true; + } + /* + If path is a closed subpath depending on + the values of text-anchor and direction of + the element the character at index is in: + */ + if (pm.isClosed()) { + /* + This implements the special wrapping criteria for single + closed subpaths. + + (start, ltr) or (end, rtl) + + If mid−offset < 0 + or mid−offset > length, + set the "hidden" flag of result[index] to true. + + (middle, ltr) or (middle, rtl) + + If + If mid−offset < −length/2 + or mid−offset > length/2, + set the "hidden" flag of result[index] to true. + + (start, rtl) or (end, ltr) + + If mid−offset < −length + or mid−offset > 0, + set the "hidden" flag of result[index] to true. + */ + TextAnchor anchor = TextAnchor.start; + Direction direction = Direction.ltr; + + double anchorShift = horizontal ? result[i].x : result[i].y; + switch (anchor) { + case start: + if (direction == Direction.ltr) { + if (mid < 0 || mid > length) { + result[index].hidden = true; + } + } else { + if (mid < -length || mid > 0) { + result[index].hidden = true; + } + } + break; + + case middle: + if (mid < -length / 2 || mid > length / 2) { + result[index].hidden = true; + } + break; + + case end: + if (direction == Direction.ltr) { + if (mid < -length || mid > 0) { + result[index].hidden = true; + } + } else { + if (mid < 0 || mid > length) { + result[index].hidden = true; + } + } + break; + } + } + /* + Set mid = mid mod length. + */ + mid %= length; + /* + If the hidden flag is false: + */ + if (!result[index].hidden) { + /* + Let point be the position and + t be the unit vector tangent to + the point mid distance + along path. + */ + float[] point = new float[2]; + float[] t = new float[2]; + pm.getPosTan((float) mid, point, t); + final double tau = 2 * Math.PI; + final double radToDeg = 360 / tau; + final double r = Math.atan2(t[1], t[0]) * radToDeg; + /* + If the "horizontal" flag is + */ + if (horizontal) { + /* + true + + Let n be the normal unit vector + pointing in the direction t + 90°. + */ + double normAngle = r + 90; + double[] n = new double[] {Math.cos(normAngle), Math.sin(normAngle)}; + /* + Let o be the horizontal distance from the + TODO vertical center line of the glyph to the alignment point. + */ + double o = 0; + /* + Then set the position in + result[index] to + point - + o×t + + y×n. + + Let r be the angle from + the positive x-axis to the tangent. + + Set the angle value + in result[index] + to angle + r. + */ + result[index].rotate += r; + } else { + /* + false + + Let n be the normal unit vector + pointing in the direction t - 90°. + */ + double normAngle = r - 90; + double[] n = new double[] {Math.cos(normAngle), Math.sin(normAngle)}; + /* + Let o be the vertical distance from the + TODO horizontal center line of the glyph to the alignment point. + */ + double o = 0; + /* + + Then set the position in + result[index] to + point - + o×t + + x×n. + + Let r be the angle from + the positive y-axis to the tangent. + + Set the angle value + in result[index] + to angle + r. + */ + result[index].rotate += r; + } + } + /* + + Otherwise, the "middle" flag + of result[index] is true: + + Set the position and angle values + of result[index] to those + in result[index − 1]. + */ + } else { + result[index].x = result[index - 1].x; + result[index].y = result[index - 1].y; + result[index].rotate = result[index - 1].rotate; + } + } + /* + If the character at index i is not within a + ‘textPath’ element and corresponds to a typographic character, then: + + This sets the starting point for rendering any characters that + occur after a ‘textPath’ element to the end of the path. + */ + if (textPathView == null && result[index].addressable) { + /* + If the "in path" flag is true: + + Set the "in path" flag to false. + + Set the "after path" flag to true. + + Set path_end equal to the end point of the path + referenced by ‘textPath’ − the position of + result[index]. + */ + if (inPath) { + inPath = false; + afterPath = true; + pm.setPath(textPath, false); + float[] pos = new float[2]; + pm.getPosTan(pm.getLength(), pos, null); + path_end.set(pos[0], pos[1]); + } + /* + + If the "after path" is true. + + If anchored chunk of + result[index] is true, set the + "after path" flag to false. + + Else, + let result.x[index] = + result.x[index] + path_end.x + and result.y[index] = + result.y[index] + path_end.y. + */ + if (afterPath) { + if (result[index].anchoredChunk) { + afterPath = false; + } else { + result[index].x += path_end.x; + result[index].y += path_end.y; + } + } + } + /* + + Set index = index + 1. + */ + index++; + } + /* + Return result + */ + return result; + } } diff --git a/android/src/main/java/com/horcrux/svg/TextPathView.java b/android/src/main/java/com/horcrux/svg/TextPathView.java index 90bcc120..afeccb09 100644 --- a/android/src/main/java/com/horcrux/svg/TextPathView.java +++ b/android/src/main/java/com/horcrux/svg/TextPathView.java @@ -6,129 +6,126 @@ * LICENSE file in the root directory of this source tree. */ - package com.horcrux.svg; +import static com.horcrux.svg.TextProperties.*; + import android.annotation.SuppressLint; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; - import com.facebook.react.bridge.Dynamic; import com.facebook.react.bridge.ReactContext; import com.facebook.react.uimanager.annotations.ReactProp; - import javax.annotation.Nullable; -import static com.horcrux.svg.TextProperties.*; - @SuppressLint("ViewConstructor") class TextPathView extends TextView { - private String mHref; - private TextPathSide mSide; - private TextPathMidLine mMidLine; - private @Nullable SVGLength mStartOffset; - private TextPathMethod mMethod = TextPathMethod.align; - private TextPathSpacing mSpacing = TextPathSpacing.exact; + private String mHref; + private TextPathSide mSide; + private TextPathMidLine mMidLine; + private @Nullable SVGLength mStartOffset; + private TextPathMethod mMethod = TextPathMethod.align; + private TextPathSpacing mSpacing = TextPathSpacing.exact; - public TextPathView(ReactContext reactContext) { - super(reactContext); - } + public TextPathView(ReactContext reactContext) { + super(reactContext); + } - @ReactProp(name = "href") - public void setHref(String href) { - mHref = href; - invalidate(); - } + @ReactProp(name = "href") + public void setHref(String href) { + mHref = href; + invalidate(); + } - @ReactProp(name = "startOffset") - public void setStartOffset(Dynamic startOffset) { - mStartOffset = SVGLength.from(startOffset); - invalidate(); - } + @ReactProp(name = "startOffset") + public void setStartOffset(Dynamic startOffset) { + mStartOffset = SVGLength.from(startOffset); + invalidate(); + } public void setStartOffset(String startOffset) { mStartOffset = SVGLength.from(startOffset); invalidate(); } - @ReactProp(name = "method") - public void setMethod(@Nullable String method) { - mMethod = TextPathMethod.valueOf(method); - invalidate(); + @ReactProp(name = "method") + public void setMethod(@Nullable String method) { + mMethod = TextPathMethod.valueOf(method); + invalidate(); + } + + @ReactProp(name = "spacing") + public void setSpacing(@Nullable String spacing) { + mSpacing = TextPathSpacing.valueOf(spacing); + invalidate(); + } + + @ReactProp(name = "side") + public void setSide(@Nullable String side) { + mSide = TextPathSide.valueOf(side); + invalidate(); + } + + @ReactProp(name = "midLine") + public void setSharp(@Nullable String midLine) { + mMidLine = TextPathMidLine.valueOf(midLine); + invalidate(); + } + + @SuppressWarnings("unused") + TextPathMethod getMethod() { + return mMethod; + } + + @SuppressWarnings("unused") + TextPathSpacing getSpacing() { + return mSpacing; + } + + TextPathSide getSide() { + return mSide; + } + + TextPathMidLine getMidLine() { + return mMidLine; + } + + SVGLength getStartOffset() { + return mStartOffset; + } + + @Override + void draw(Canvas canvas, Paint paint, float opacity) { + drawGroup(canvas, paint, opacity); + } + + Path getTextPath(Canvas canvas, Paint paint) { + SvgView svg = getSvgView(); + VirtualView template = svg.getDefinedTemplate(mHref); + + if (!(template instanceof RenderableView)) { + // warning about this. + return null; } - @ReactProp(name = "spacing") - public void setSpacing(@Nullable String spacing) { - mSpacing = TextPathSpacing.valueOf(spacing); - invalidate(); - } + RenderableView view = (RenderableView) template; + return view.getPath(canvas, paint); + } - @ReactProp(name = "side") - public void setSide(@Nullable String side) { - mSide = TextPathSide.valueOf(side); - invalidate(); - } + @Override + Path getPath(Canvas canvas, Paint paint) { + return getGroupPath(canvas, paint); + } - @ReactProp(name = "midLine") - public void setSharp(@Nullable String midLine) { - mMidLine = TextPathMidLine.valueOf(midLine); - invalidate(); - } + @Override + void pushGlyphContext() { + // do nothing + } - @SuppressWarnings("unused") - TextPathMethod getMethod() { - return mMethod; - } - - @SuppressWarnings("unused") - TextPathSpacing getSpacing() { - return mSpacing; - } - - TextPathSide getSide() { - return mSide; - } - - TextPathMidLine getMidLine() { - return mMidLine; - } - - SVGLength getStartOffset() { - return mStartOffset; - } - - @Override - void draw(Canvas canvas, Paint paint, float opacity) { - drawGroup(canvas, paint, opacity); - } - - Path getTextPath(Canvas canvas, Paint paint) { - SvgView svg = getSvgView(); - VirtualView template = svg.getDefinedTemplate(mHref); - - if (!(template instanceof RenderableView)) { - // warning about this. - return null; - } - - RenderableView view = (RenderableView)template; - return view.getPath(canvas, paint); - } - - @Override - Path getPath(Canvas canvas, Paint paint) { - return getGroupPath(canvas, paint); - } - - @Override - void pushGlyphContext() { - // do nothing - } - - @Override - void popGlyphContext() { - // do nothing - } + @Override + void popGlyphContext() { + // do nothing + } } diff --git a/android/src/main/java/com/horcrux/svg/TextProperties.java b/android/src/main/java/com/horcrux/svg/TextProperties.java index fceb7427..a8581e3f 100644 --- a/android/src/main/java/com/horcrux/svg/TextProperties.java +++ b/android/src/main/java/com/horcrux/svg/TextProperties.java @@ -2,212 +2,218 @@ package com.horcrux.svg; import java.util.HashMap; import java.util.Map; - import javax.annotation.Nonnull; class TextProperties { + /* + https://drafts.csswg.org/css-inline/#propdef-alignment-baseline + 2.2.1. Alignment Point: alignment-baseline longhand + + Name: alignment-baseline + Value: baseline | text-bottom | alphabetic | ideographic | middle | central | mathematical | text-top | bottom | center | top + Initial: baseline + Applies to: inline-level boxes, flex items, grid items, table cells + Inherited: no + Percentages: N/A + Media: visual + Computed value: as specified + Canonical order: per grammar + Animation type: discrete + */ + enum AlignmentBaseline { + baseline("baseline"), + textBottom("text-bottom"), + alphabetic("alphabetic"), + ideographic("ideographic"), + middle("middle"), + central("central"), + mathematical("mathematical"), + textTop("text-top"), + bottom("bottom"), + center("center"), + top("top"), /* - https://drafts.csswg.org/css-inline/#propdef-alignment-baseline - 2.2.1. Alignment Point: alignment-baseline longhand + SVG implementations may support the following aliases in order to support legacy content: - Name: alignment-baseline - Value: baseline | text-bottom | alphabetic | ideographic | middle | central | mathematical | text-top | bottom | center | top - Initial: baseline - Applies to: inline-level boxes, flex items, grid items, table cells - Inherited: no - Percentages: N/A - Media: visual - Computed value: as specified - Canonical order: per grammar - Animation type: discrete - */ - enum AlignmentBaseline { - baseline("baseline"), - textBottom("text-bottom"), - alphabetic("alphabetic"), - ideographic("ideographic"), - middle("middle"), - central("central"), - mathematical("mathematical"), - textTop("text-top"), - bottom("bottom"), - center("center"), - top("top"), - /* - SVG implementations may support the following aliases in order to support legacy content: + text-before-edge = text-top + text-after-edge = text-bottom + */ + textBeforeEdge("text-before-edge"), + textAfterEdge("text-after-edge"), + // SVG 1.1 + beforeEdge("before-edge"), + afterEdge("after-edge"), + hanging("hanging"), + ; - text-before-edge = text-top - text-after-edge = text-bottom - */ - textBeforeEdge("text-before-edge"), - textAfterEdge("text-after-edge"), - // SVG 1.1 - beforeEdge("before-edge"), - afterEdge("after-edge"), - hanging("hanging"), - ; + private final String alignment; - private final String alignment; - - AlignmentBaseline(String alignment) { - this.alignment = alignment; - } - - static AlignmentBaseline getEnum(String strVal) { - if (!alignmentToEnum.containsKey(strVal)) { - throw new IllegalArgumentException("Unknown String Value: " + strVal); - } - return alignmentToEnum.get(strVal); - } - - private static final Map alignmentToEnum = new HashMap<>(); - - static { - for (final AlignmentBaseline en : AlignmentBaseline.values()) { - alignmentToEnum.put(en.alignment, en); - } - } - - @Nonnull - @Override - public String toString() { - return alignment; - } + AlignmentBaseline(String alignment) { + this.alignment = alignment; } - // TODO implement rtl + static AlignmentBaseline getEnum(String strVal) { + if (!alignmentToEnum.containsKey(strVal)) { + throw new IllegalArgumentException("Unknown String Value: " + strVal); + } + return alignmentToEnum.get(strVal); + } + + private static final Map alignmentToEnum = new HashMap<>(); + + static { + for (final AlignmentBaseline en : AlignmentBaseline.values()) { + alignmentToEnum.put(en.alignment, en); + } + } + + @Nonnull + @Override + public String toString() { + return alignment; + } + } + + // TODO implement rtl + @SuppressWarnings("unused") + enum Direction { + ltr, + rtl + } + + enum FontVariantLigatures { + normal, @SuppressWarnings("unused") - enum Direction { - ltr, - rtl + none + } + + enum FontStyle { + normal, + italic, + @SuppressWarnings("unused") + oblique + } + + enum FontWeight { + // Absolute + Normal("normal"), + Bold("bold"), + w100("100"), + w200("200"), + w300("300"), + w400("400"), + w500("500"), + w600("600"), + w700("700"), + w800("800"), + w900("900"), + // Relative + Bolder("bolder"), + Lighter("lighter"); + + private final String weight; + + FontWeight(String weight) { + this.weight = weight; } - enum FontVariantLigatures { - normal, - @SuppressWarnings("unused")none + static boolean hasEnum(String strVal) { + return weightToEnum.containsKey(strVal); } - enum FontStyle { - normal, - italic, - @SuppressWarnings("unused")oblique + static FontWeight get(String strVal) { + return weightToEnum.get(strVal); } - enum FontWeight { - // Absolute - Normal ("normal"), - Bold ("bold"), - w100 ("100"), - w200 ("200"), - w300 ("300"), - w400 ("400"), - w500 ("500"), - w600 ("600"), - w700 ("700"), - w800 ("800"), - w900 ("900"), - // Relative - Bolder ("bolder"), - Lighter ("lighter"); + private static final Map weightToEnum = new HashMap<>(); - private final String weight; - FontWeight(String weight) { - this.weight = weight; - } - - static boolean hasEnum(String strVal) { - return weightToEnum.containsKey(strVal); - } - - static FontWeight get(String strVal) { - return weightToEnum.get(strVal); - } - - private static final Map weightToEnum = new HashMap<>(); - static { - for (final FontWeight en : FontWeight.values()) { - weightToEnum.put(en.weight, en); - } - } - - @Nonnull - @Override - public String toString() { - return weight; - } + static { + for (final FontWeight en : FontWeight.values()) { + weightToEnum.put(en.weight, en); + } } - enum TextAnchor - { - start, - middle, - end + @Nonnull + @Override + public String toString() { + return weight; + } + } + + enum TextAnchor { + start, + middle, + end + } + + enum TextDecoration { + None("none"), + Underline("underline"), + Overline("overline"), + LineThrough("line-through"), + Blink("blink"); + + private final String decoration; + + TextDecoration(String decoration) { + this.decoration = decoration; } - enum TextDecoration - { - None("none"), - Underline("underline"), - Overline("overline"), - LineThrough("line-through"), - Blink("blink"); - - private final String decoration; - TextDecoration(String decoration) { - this.decoration = decoration; - } - - static TextDecoration getEnum(String strVal) { - if(!decorationToEnum.containsKey(strVal)) { - throw new IllegalArgumentException("Unknown String Value: " + strVal); - } - return decorationToEnum.get(strVal); - } - - private static final Map decorationToEnum = new HashMap<>(); - static { - for (final TextDecoration en : TextDecoration.values()) { - decorationToEnum.put(en.decoration, en); - } - } - - @Nonnull - @Override - public String toString() { - return decoration; - } + static TextDecoration getEnum(String strVal) { + if (!decorationToEnum.containsKey(strVal)) { + throw new IllegalArgumentException("Unknown String Value: " + strVal); + } + return decorationToEnum.get(strVal); } - enum TextLengthAdjust - { - spacing, - spacingAndGlyphs + private static final Map decorationToEnum = new HashMap<>(); + + static { + for (final TextDecoration en : TextDecoration.values()) { + decorationToEnum.put(en.decoration, en); + } } - enum TextPathMethod { - align, - @SuppressWarnings("unused")stretch + @Nonnull + @Override + public String toString() { + return decoration; } + } - /* - TODO suggest adding a compatibility mid-line rendering attribute to textPath, - for a chrome/firefox/opera/safari compatible sharp text path rendering, - which doesn't bend text smoothly along a right angle curve, (like Edge does) - but keeps the mid-line orthogonal to the mid-point tangent at all times instead. - */ - enum TextPathMidLine { - sharp, - @SuppressWarnings("unused")smooth - } + enum TextLengthAdjust { + spacing, + spacingAndGlyphs + } - enum TextPathSide { - @SuppressWarnings("unused")left, - right - } + enum TextPathMethod { + align, + @SuppressWarnings("unused") + stretch + } - enum TextPathSpacing { - @SuppressWarnings("unused")auto, - exact - } + /* + TODO suggest adding a compatibility mid-line rendering attribute to textPath, + for a chrome/firefox/opera/safari compatible sharp text path rendering, + which doesn't bend text smoothly along a right angle curve, (like Edge does) + but keeps the mid-line orthogonal to the mid-point tangent at all times instead. + */ + enum TextPathMidLine { + sharp, + @SuppressWarnings("unused") + smooth + } + + enum TextPathSide { + @SuppressWarnings("unused") + left, + right + } + + enum TextPathSpacing { + @SuppressWarnings("unused") + auto, + exact + } } diff --git a/android/src/main/java/com/horcrux/svg/TextView.java b/android/src/main/java/com/horcrux/svg/TextView.java index 39cb3f48..9e0e7052 100644 --- a/android/src/main/java/com/horcrux/svg/TextView.java +++ b/android/src/main/java/com/horcrux/svg/TextView.java @@ -6,9 +6,11 @@ * LICENSE file in the root directory of this source tree. */ - package com.horcrux.svg; +import static com.horcrux.svg.TextProperties.AlignmentBaseline; +import static com.horcrux.svg.TextProperties.TextLengthAdjust; + import android.annotation.SuppressLint; import android.graphics.Canvas; import android.graphics.Paint; @@ -16,292 +18,288 @@ import android.graphics.Path; import android.graphics.Region; import android.view.View; import android.view.ViewParent; - import com.facebook.react.bridge.Dynamic; import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.uimanager.annotations.ReactProp; - import java.util.ArrayList; - import javax.annotation.Nullable; -import static com.horcrux.svg.TextProperties.AlignmentBaseline; -import static com.horcrux.svg.TextProperties.TextLengthAdjust; - @SuppressLint("ViewConstructor") class TextView extends GroupView { - SVGLength mInlineSize = null; - SVGLength mTextLength = null; - private String mBaselineShift = null; - TextLengthAdjust mLengthAdjust = TextLengthAdjust.spacing; - private AlignmentBaseline mAlignmentBaseline; - @Nullable private ArrayList mPositionX; - @Nullable private ArrayList mPositionY; - @Nullable private ArrayList mRotate; - @Nullable private ArrayList mDeltaX; - @Nullable private ArrayList mDeltaY; - double cachedAdvance = Double.NaN; + SVGLength mInlineSize = null; + SVGLength mTextLength = null; + private String mBaselineShift = null; + TextLengthAdjust mLengthAdjust = TextLengthAdjust.spacing; + private AlignmentBaseline mAlignmentBaseline; + @Nullable private ArrayList mPositionX; + @Nullable private ArrayList mPositionY; + @Nullable private ArrayList mRotate; + @Nullable private ArrayList mDeltaX; + @Nullable private ArrayList mDeltaY; + double cachedAdvance = Double.NaN; - public TextView(ReactContext reactContext) { - super(reactContext); - } + public TextView(ReactContext reactContext) { + super(reactContext); + } - @Override - public void invalidate() { - if (mPath == null) { - return; - } - super.invalidate(); - getTextContainer().clearChildCache(); + @Override + public void invalidate() { + if (mPath == null) { + return; } + super.invalidate(); + getTextContainer().clearChildCache(); + } - void clearCache() { - cachedAdvance = Double.NaN; - super.clearCache(); - } + void clearCache() { + cachedAdvance = Double.NaN; + super.clearCache(); + } - @ReactProp(name = "inlineSize") - public void setInlineSize(Dynamic inlineSize) { - mInlineSize = SVGLength.from(inlineSize); - invalidate(); - } + @ReactProp(name = "inlineSize") + public void setInlineSize(Dynamic inlineSize) { + mInlineSize = SVGLength.from(inlineSize); + invalidate(); + } public void setInlineSize(String inlineSize) { mInlineSize = SVGLength.from(inlineSize); invalidate(); } - @ReactProp(name = "textLength") - public void setTextLength(Dynamic length) { - mTextLength = SVGLength.from(length); - invalidate(); - } + @ReactProp(name = "textLength") + public void setTextLength(Dynamic length) { + mTextLength = SVGLength.from(length); + invalidate(); + } public void setTextLength(String length) { mTextLength = SVGLength.from(length); invalidate(); } - @ReactProp(name = "lengthAdjust") - public void setLengthAdjust(@Nullable String adjustment) { - mLengthAdjust = TextLengthAdjust.valueOf(adjustment); - invalidate(); - } + @ReactProp(name = "lengthAdjust") + public void setLengthAdjust(@Nullable String adjustment) { + mLengthAdjust = TextLengthAdjust.valueOf(adjustment); + invalidate(); + } - @ReactProp(name = "alignmentBaseline") - public void setMethod(@Nullable String alignment) { - mAlignmentBaseline = AlignmentBaseline.getEnum(alignment); - invalidate(); - } + @ReactProp(name = "alignmentBaseline") + public void setMethod(@Nullable String alignment) { + mAlignmentBaseline = AlignmentBaseline.getEnum(alignment); + invalidate(); + } - @ReactProp(name = "baselineShift") - public void setBaselineShift(Dynamic baselineShift) { - mBaselineShift = SVGLength.toString(baselineShift); - invalidate(); - } + @ReactProp(name = "baselineShift") + public void setBaselineShift(Dynamic baselineShift) { + mBaselineShift = SVGLength.toString(baselineShift); + invalidate(); + } public void setBaselineShift(String baselineShift) { mBaselineShift = baselineShift; invalidate(); } - @ReactProp(name = "verticalAlign") - public void setVerticalAlign(@Nullable String verticalAlign) { - if (verticalAlign != null) { - verticalAlign = verticalAlign.trim(); - int i = verticalAlign.lastIndexOf(' '); - try { - mAlignmentBaseline = AlignmentBaseline.getEnum(verticalAlign.substring(i)); - } catch (IllegalArgumentException e) { - mAlignmentBaseline = AlignmentBaseline.baseline; - } - try { - mBaselineShift = verticalAlign.substring(0, i); - } catch (IndexOutOfBoundsException e) { - mBaselineShift = null; - } - } else { - mAlignmentBaseline = AlignmentBaseline.baseline; - mBaselineShift = null; - } - invalidate(); + @ReactProp(name = "verticalAlign") + public void setVerticalAlign(@Nullable String verticalAlign) { + if (verticalAlign != null) { + verticalAlign = verticalAlign.trim(); + int i = verticalAlign.lastIndexOf(' '); + try { + mAlignmentBaseline = AlignmentBaseline.getEnum(verticalAlign.substring(i)); + } catch (IllegalArgumentException e) { + mAlignmentBaseline = AlignmentBaseline.baseline; + } + try { + mBaselineShift = verticalAlign.substring(0, i); + } catch (IndexOutOfBoundsException e) { + mBaselineShift = null; + } + } else { + mAlignmentBaseline = AlignmentBaseline.baseline; + mBaselineShift = null; } + invalidate(); + } - @ReactProp(name = "rotate") - public void setRotate(Dynamic rotate) { - mRotate = SVGLength.arrayFrom(rotate); - invalidate(); - } + @ReactProp(name = "rotate") + public void setRotate(Dynamic rotate) { + mRotate = SVGLength.arrayFrom(rotate); + invalidate(); + } public void setRotate(ReadableArray rotate) { mRotate = SVGLength.arrayFrom(rotate); invalidate(); } - @ReactProp(name = "dx") - public void setDeltaX(Dynamic deltaX) { - mDeltaX = SVGLength.arrayFrom(deltaX); - invalidate(); - } + @ReactProp(name = "dx") + public void setDeltaX(Dynamic deltaX) { + mDeltaX = SVGLength.arrayFrom(deltaX); + invalidate(); + } public void setDeltaX(ReadableArray deltaX) { mDeltaX = SVGLength.arrayFrom(deltaX); invalidate(); } - @ReactProp(name = "dy") - public void setDeltaY(Dynamic deltaY) { - mDeltaY = SVGLength.arrayFrom(deltaY); - invalidate(); - } + @ReactProp(name = "dy") + public void setDeltaY(Dynamic deltaY) { + mDeltaY = SVGLength.arrayFrom(deltaY); + invalidate(); + } public void setDeltaY(ReadableArray deltaY) { mDeltaY = SVGLength.arrayFrom(deltaY); invalidate(); } - @ReactProp(name = "x") - public void setPositionX(Dynamic positionX) { - mPositionX = SVGLength.arrayFrom(positionX); - invalidate(); - } + @ReactProp(name = "x") + public void setPositionX(Dynamic positionX) { + mPositionX = SVGLength.arrayFrom(positionX); + invalidate(); + } public void setPositionX(ReadableArray positionX) { mPositionX = SVGLength.arrayFrom(positionX); invalidate(); } - @ReactProp(name = "y") - public void setPositionY(Dynamic positionY) { - mPositionY = SVGLength.arrayFrom(positionY); - invalidate(); - } + public void setPositionY(Dynamic positionY) { + mPositionY = SVGLength.arrayFrom(positionY); + invalidate(); + } public void setPositionY(ReadableArray positionY) { mPositionY = SVGLength.arrayFrom(positionY); invalidate(); } - @Override - void draw(Canvas canvas, Paint paint, float opacity) { - setupGlyphContext(canvas); - clip(canvas, paint); - getGroupPath(canvas, paint); - pushGlyphContext(); - drawGroup(canvas, paint, opacity); - popGlyphContext(); - } + @Override + void draw(Canvas canvas, Paint paint, float opacity) { + setupGlyphContext(canvas); + clip(canvas, paint); + getGroupPath(canvas, paint); + pushGlyphContext(); + drawGroup(canvas, paint, opacity); + popGlyphContext(); + } - @Override - Path getPath(Canvas canvas, Paint paint) { - if (mPath != null) { - return mPath; + @Override + Path getPath(Canvas canvas, Paint paint) { + if (mPath != null) { + return mPath; + } + setupGlyphContext(canvas); + return getGroupPath(canvas, paint); + } + + @Override + Path getPath(Canvas canvas, Paint paint, Region.Op op) { + return getPath(canvas, paint); + } + + AlignmentBaseline getAlignmentBaseline() { + if (mAlignmentBaseline == null) { + ViewParent parent = this.getParent(); + while (parent != null) { + if (parent instanceof TextView) { + TextView node = (TextView) parent; + final AlignmentBaseline baseline = node.mAlignmentBaseline; + if (baseline != null) { + mAlignmentBaseline = baseline; + return baseline; + } } - setupGlyphContext(canvas); - return getGroupPath(canvas, paint); + parent = parent.getParent(); + } } - - @Override - Path getPath(Canvas canvas, Paint paint, Region.Op op) { - return getPath(canvas, paint); + if (mAlignmentBaseline == null) { + mAlignmentBaseline = AlignmentBaseline.baseline; } + return mAlignmentBaseline; + } - AlignmentBaseline getAlignmentBaseline() { - if (mAlignmentBaseline == null) { - ViewParent parent = this.getParent(); - while (parent != null) { - if (parent instanceof TextView) { - TextView node = (TextView)parent; - final AlignmentBaseline baseline = node.mAlignmentBaseline; - if (baseline != null) { - mAlignmentBaseline = baseline; - return baseline; - } - } - parent = parent.getParent(); - } + String getBaselineShift() { + if (mBaselineShift == null) { + ViewParent parent = this.getParent(); + while (parent != null) { + if (parent instanceof TextView) { + TextView node = (TextView) parent; + final String baselineShift = node.mBaselineShift; + if (baselineShift != null) { + mBaselineShift = baselineShift; + return baselineShift; + } } - if (mAlignmentBaseline == null) { - mAlignmentBaseline = AlignmentBaseline.baseline; - } - return mAlignmentBaseline; + parent = parent.getParent(); + } } + return mBaselineShift; + } - String getBaselineShift() { - if (mBaselineShift == null) { - ViewParent parent = this.getParent(); - while (parent != null) { - if (parent instanceof TextView) { - TextView node = (TextView)parent; - final String baselineShift = node.mBaselineShift; - if (baselineShift != null) { - mBaselineShift = baselineShift; - return baselineShift; - } - } - parent = parent.getParent(); - } - } - return mBaselineShift; + Path getGroupPath(Canvas canvas, Paint paint) { + if (mPath != null) { + return mPath; } + pushGlyphContext(); + mPath = super.getPath(canvas, paint); + popGlyphContext(); - Path getGroupPath(Canvas canvas, Paint paint) { - if (mPath != null) { - return mPath; - } - pushGlyphContext(); - mPath = super.getPath(canvas, paint); - popGlyphContext(); + return mPath; + } - return mPath; - } + @Override + void pushGlyphContext() { + boolean isTextNode = !(this instanceof TextPathView) && !(this instanceof TSpanView); + getTextRootGlyphContext() + .pushContext(isTextNode, this, mFont, mPositionX, mPositionY, mDeltaX, mDeltaY, mRotate); + } - @Override - void pushGlyphContext() { - boolean isTextNode = !(this instanceof TextPathView) && !(this instanceof TSpanView); - getTextRootGlyphContext().pushContext(isTextNode, this, mFont, mPositionX, mPositionY, mDeltaX, mDeltaY, mRotate); - } - - TextView getTextAnchorRoot() { - GlyphContext gc = getTextRootGlyphContext(); - ArrayList font = gc.mFontContext; - TextView node = this; - ViewParent parent = this.getParent(); - for (int i = font.size() - 1; i >= 0; i--) { - if (!(parent instanceof TextView) || font.get(i).textAnchor == TextProperties.TextAnchor.start || node.mPositionX != null) { - return node; - } - node = (TextView) parent; - parent = node.getParent(); - } + TextView getTextAnchorRoot() { + GlyphContext gc = getTextRootGlyphContext(); + ArrayList font = gc.mFontContext; + TextView node = this; + ViewParent parent = this.getParent(); + for (int i = font.size() - 1; i >= 0; i--) { + if (!(parent instanceof TextView) + || font.get(i).textAnchor == TextProperties.TextAnchor.start + || node.mPositionX != null) { return node; + } + node = (TextView) parent; + parent = node.getParent(); } + return node; + } - double getSubtreeTextChunksTotalAdvance(Paint paint) { - if (!Double.isNaN(cachedAdvance)) { - return cachedAdvance; - } - double advance = 0; - for (int i = 0; i < getChildCount(); i++) { - View child = getChildAt(i); - if (child instanceof TextView) { - TextView text = (TextView) child; - advance += text.getSubtreeTextChunksTotalAdvance(paint); - } - } - cachedAdvance = advance; - return advance; + double getSubtreeTextChunksTotalAdvance(Paint paint) { + if (!Double.isNaN(cachedAdvance)) { + return cachedAdvance; } + double advance = 0; + for (int i = 0; i < getChildCount(); i++) { + View child = getChildAt(i); + if (child instanceof TextView) { + TextView text = (TextView) child; + advance += text.getSubtreeTextChunksTotalAdvance(paint); + } + } + cachedAdvance = advance; + return advance; + } - TextView getTextContainer() { - TextView node = this; - ViewParent parent = this.getParent(); - while (parent instanceof TextView) { - node = (TextView) parent; - parent = node.getParent(); - } - return node; + TextView getTextContainer() { + TextView node = this; + ViewParent parent = this.getParent(); + while (parent instanceof TextView) { + node = (TextView) parent; + parent = node.getParent(); } + return node; + } } diff --git a/android/src/main/java/com/horcrux/svg/UseView.java b/android/src/main/java/com/horcrux/svg/UseView.java index a43f3263..abe380da 100644 --- a/android/src/main/java/com/horcrux/svg/UseView.java +++ b/android/src/main/java/com/horcrux/svg/UseView.java @@ -6,7 +6,6 @@ * LICENSE file in the root directory of this source tree. */ - package com.horcrux.svg; import android.annotation.SuppressLint; @@ -14,7 +13,6 @@ import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Path; - import com.facebook.common.logging.FLog; import com.facebook.react.bridge.Dynamic; import com.facebook.react.bridge.ReactContext; @@ -23,138 +21,151 @@ import com.facebook.react.uimanager.annotations.ReactProp; @SuppressLint("ViewConstructor") class UseView extends RenderableView { - private String mHref; - private SVGLength mX; - private SVGLength mY; - private SVGLength mW; - private SVGLength mH; + private String mHref; + private SVGLength mX; + private SVGLength mY; + private SVGLength mW; + private SVGLength mH; - public UseView(ReactContext reactContext) { - super(reactContext); - } + public UseView(ReactContext reactContext) { + super(reactContext); + } - @ReactProp(name = "href") - public void setHref(String href) { - mHref = href; - invalidate(); - } + @ReactProp(name = "href") + public void setHref(String href) { + mHref = href; + invalidate(); + } - @ReactProp(name = "x") - public void setX(Dynamic x) { - mX = SVGLength.from(x); - invalidate(); - } + @ReactProp(name = "x") + public void setX(Dynamic x) { + mX = SVGLength.from(x); + invalidate(); + } public void setX(String x) { mX = SVGLength.from(x); invalidate(); } - @ReactProp(name = "y") - public void setY(Dynamic y) { - mY = SVGLength.from(y); - invalidate(); - } + @ReactProp(name = "y") + public void setY(Dynamic y) { + mY = SVGLength.from(y); + invalidate(); + } public void setY(String y) { mY = SVGLength.from(y); invalidate(); } - @ReactProp(name = "width") - public void setWidth(Dynamic width) { - mW = SVGLength.from(width); - invalidate(); - } + @ReactProp(name = "width") + public void setWidth(Dynamic width) { + mW = SVGLength.from(width); + invalidate(); + } public void setWidth(String width) { mW = SVGLength.from(width); invalidate(); } - @ReactProp(name = "height") - public void setHeight(Dynamic height) { - mH = SVGLength.from(height); - invalidate(); - } + @ReactProp(name = "height") + public void setHeight(Dynamic height) { + mH = SVGLength.from(height); + invalidate(); + } public void setHeight(String height) { mH = SVGLength.from(height); invalidate(); } - @Override - void draw(Canvas canvas, Paint paint, float opacity) { - VirtualView template = getSvgView().getDefinedTemplate(mHref); + @Override + void draw(Canvas canvas, Paint paint, float opacity) { + VirtualView template = getSvgView().getDefinedTemplate(mHref); - if (template == null) { - FLog.w(ReactConstants.TAG, "`Use` element expected a pre-defined svg template as `href` prop, " + - "template named: " + mHref + " is not defined."); - return; - } - - template.clearCache(); - canvas.translate((float) relativeOnWidth(mX), (float) relativeOnHeight(mY)); - if (template instanceof RenderableView) { - ((RenderableView)template).mergeProperties(this); - } - - int count = template.saveAndSetupCanvas(canvas, mCTM); - clip(canvas, paint); - - if (template instanceof SymbolView) { - SymbolView symbol = (SymbolView)template; - symbol.drawSymbol(canvas, paint, opacity, (float) relativeOnWidth(mW), (float) relativeOnHeight(mH)); - } else { - template.draw(canvas, paint, opacity * mOpacity); - } - - this.setClientRect(template.getClientRect()); - - template.restoreCanvas(canvas, count); - if (template instanceof RenderableView) { - ((RenderableView)template).resetProperties(); - } + if (template == null) { + FLog.w( + ReactConstants.TAG, + "`Use` element expected a pre-defined svg template as `href` prop, " + + "template named: " + + mHref + + " is not defined."); + return; } - @Override - int hitTest(float[] src) { - if (!mInvertible || !mTransformInvertible) { - return -1; - } - - float[] dst = new float[2]; - mInvMatrix.mapPoints(dst, src); - mInvTransform.mapPoints(dst); - - VirtualView template = getSvgView().getDefinedTemplate(mHref); - if (template == null) { - FLog.w(ReactConstants.TAG, "`Use` element expected a pre-defined svg template as `href` prop, " + - "template named: " + mHref + " is not defined."); - return -1; - } - - int hitChild = template.hitTest(dst); - if (hitChild != -1) { - return (template.isResponsible() || hitChild != template.getId()) ? hitChild : getId(); - } - - return -1; + template.clearCache(); + canvas.translate((float) relativeOnWidth(mX), (float) relativeOnHeight(mY)); + if (template instanceof RenderableView) { + ((RenderableView) template).mergeProperties(this); } - @Override - Path getPath(Canvas canvas, Paint paint) { - VirtualView template = getSvgView().getDefinedTemplate(mHref); - if (template == null) { - FLog.w(ReactConstants.TAG, "`Use` element expected a pre-defined svg template as `href` prop, " + - "template named: " + mHref + " is not defined."); - return null; - } - Path path = template.getPath(canvas, paint); - Path use = new Path(); - Matrix m = new Matrix(); - m.setTranslate((float) relativeOnWidth(mX), (float) relativeOnHeight(mY)); - path.transform(m, use); - return use; + int count = template.saveAndSetupCanvas(canvas, mCTM); + clip(canvas, paint); + + if (template instanceof SymbolView) { + SymbolView symbol = (SymbolView) template; + symbol.drawSymbol( + canvas, paint, opacity, (float) relativeOnWidth(mW), (float) relativeOnHeight(mH)); + } else { + template.draw(canvas, paint, opacity * mOpacity); } + + this.setClientRect(template.getClientRect()); + + template.restoreCanvas(canvas, count); + if (template instanceof RenderableView) { + ((RenderableView) template).resetProperties(); + } + } + + @Override + int hitTest(float[] src) { + if (!mInvertible || !mTransformInvertible) { + return -1; + } + + float[] dst = new float[2]; + mInvMatrix.mapPoints(dst, src); + mInvTransform.mapPoints(dst); + + VirtualView template = getSvgView().getDefinedTemplate(mHref); + if (template == null) { + FLog.w( + ReactConstants.TAG, + "`Use` element expected a pre-defined svg template as `href` prop, " + + "template named: " + + mHref + + " is not defined."); + return -1; + } + + int hitChild = template.hitTest(dst); + if (hitChild != -1) { + return (template.isResponsible() || hitChild != template.getId()) ? hitChild : getId(); + } + + return -1; + } + + @Override + Path getPath(Canvas canvas, Paint paint) { + VirtualView template = getSvgView().getDefinedTemplate(mHref); + if (template == null) { + FLog.w( + ReactConstants.TAG, + "`Use` element expected a pre-defined svg template as `href` prop, " + + "template named: " + + mHref + + " is not defined."); + return null; + } + Path path = template.getPath(canvas, paint); + Path use = new Path(); + Matrix m = new Matrix(); + m.setTranslate((float) relativeOnWidth(mX), (float) relativeOnHeight(mY)); + path.transform(m, use); + return use; + } } diff --git a/android/src/main/java/com/horcrux/svg/ViewBox.java b/android/src/main/java/com/horcrux/svg/ViewBox.java index 67e0082e..b5e64c56 100644 --- a/android/src/main/java/com/horcrux/svg/ViewBox.java +++ b/android/src/main/java/com/horcrux/svg/ViewBox.java @@ -6,7 +6,6 @@ * LICENSE file in the root directory of this source tree. */ - package com.horcrux.svg; import android.graphics.Matrix; @@ -14,90 +13,91 @@ import android.graphics.RectF; class ViewBox { - private static final int MOS_MEET = 0; - private static final int MOS_SLICE = 1; - private static final int MOS_NONE = 2; + private static final int MOS_MEET = 0; + private static final int MOS_SLICE = 1; + private static final int MOS_NONE = 2; - static Matrix getTransform(RectF vbRect, RectF eRect, String align, int meetOrSlice) { - // based on https://svgwg.org/svg2-draft/coords.html#ComputingAViewportsTransform + static Matrix getTransform(RectF vbRect, RectF eRect, String align, int meetOrSlice) { + // based on https://svgwg.org/svg2-draft/coords.html#ComputingAViewportsTransform - // Let vb-x, vb-y, vb-width, vb-height be the min-x, min-y, width and height values of the viewBox attribute respectively. - double vbX = vbRect.left; - double vbY = vbRect.top; - double vbWidth = vbRect.width(); - double vbHeight = vbRect.height(); + // Let vb-x, vb-y, vb-width, vb-height be the min-x, min-y, width and height values of the + // viewBox attribute respectively. + double vbX = vbRect.left; + double vbY = vbRect.top; + double vbWidth = vbRect.width(); + double vbHeight = vbRect.height(); - // Let e-x, e-y, e-width, e-height be the position and size of the element respectively. - double eX = eRect.left; - double eY = eRect.top; - double eWidth = eRect.width(); - double eHeight = eRect.height(); + // Let e-x, e-y, e-width, e-height be the position and size of the element respectively. + double eX = eRect.left; + double eY = eRect.top; + double eWidth = eRect.width(); + double eHeight = eRect.height(); + // Initialize scale-x to e-width/vb-width. + double scaleX = eWidth / vbWidth; - // Initialize scale-x to e-width/vb-width. - double scaleX = eWidth / vbWidth; + // Initialize scale-y to e-height/vb-height. + double scaleY = eHeight / vbHeight; - // Initialize scale-y to e-height/vb-height. - double scaleY = eHeight / vbHeight; + // Initialize translate-x to e-x - (vb-x * scale-x). + // Initialize translate-y to e-y - (vb-y * scale-y). + double translateX = eX - (vbX * scaleX); + double translateY = eY - (vbY * scaleY); - // Initialize translate-x to e-x - (vb-x * scale-x). - // Initialize translate-y to e-y - (vb-y * scale-y). - double translateX = eX - (vbX * scaleX); - double translateY = eY - (vbY * scaleY); + // If align is 'none' + if (meetOrSlice == MOS_NONE) { + // Let scale be set the smaller value of scale-x and scale-y. + // Assign scale-x and scale-y to scale. + double scale = scaleX = scaleY = Math.min(scaleX, scaleY); - // If align is 'none' - if (meetOrSlice == MOS_NONE) { - // Let scale be set the smaller value of scale-x and scale-y. - // Assign scale-x and scale-y to scale. - double scale = scaleX = scaleY = Math.min(scaleX, scaleY); + // If scale is greater than 1 + if (scale > 1) { + // Minus translateX by (eWidth / scale - vbWidth) / 2 + // Minus translateY by (eHeight / scale - vbHeight) / 2 + translateX -= (eWidth / scale - vbWidth) / 2; + translateY -= (eHeight / scale - vbHeight) / 2; + } else { + translateX -= (eWidth - vbWidth * scale) / 2; + translateY -= (eHeight - vbHeight * scale) / 2; + } + } else { + // If align is not 'none' and meetOrSlice is 'meet', set the larger of scale-x and scale-y to + // the smaller. + // Otherwise, if align is not 'none' and meetOrSlice is 'slice', set the smaller of scale-x + // and scale-y to the larger. - // If scale is greater than 1 - if (scale > 1) { - // Minus translateX by (eWidth / scale - vbWidth) / 2 - // Minus translateY by (eHeight / scale - vbHeight) / 2 - translateX -= (eWidth / scale - vbWidth) / 2; - translateY -= (eHeight / scale - vbHeight) / 2; - } else { - translateX -= (eWidth - vbWidth * scale) / 2; - translateY -= (eHeight - vbHeight * scale) / 2; - } - } else { - // If align is not 'none' and meetOrSlice is 'meet', set the larger of scale-x and scale-y to the smaller. - // Otherwise, if align is not 'none' and meetOrSlice is 'slice', set the smaller of scale-x and scale-y to the larger. + if (!align.equals("none") && meetOrSlice == MOS_MEET) { + scaleX = scaleY = Math.min(scaleX, scaleY); + } else if (!align.equals("none") && meetOrSlice == MOS_SLICE) { + scaleX = scaleY = Math.max(scaleX, scaleY); + } - if (!align.equals("none") && meetOrSlice == MOS_MEET) { - scaleX = scaleY = Math.min(scaleX, scaleY); - } else if (!align.equals("none") && meetOrSlice == MOS_SLICE) { - scaleX = scaleY = Math.max(scaleX, scaleY); - } + // If align contains 'xMid', add (e-width - vb-width * scale-x) / 2 to translate-x. + if (align.contains("xMid")) { + translateX += (eWidth - vbWidth * scaleX) / 2.0d; + } - // If align contains 'xMid', add (e-width - vb-width * scale-x) / 2 to translate-x. - if (align.contains("xMid")) { - translateX += (eWidth - vbWidth * scaleX) / 2.0d; - } + // If align contains 'xMax', add (e-width - vb-width * scale-x) to translate-x. + if (align.contains("xMax")) { + translateX += (eWidth - vbWidth * scaleX); + } - // If align contains 'xMax', add (e-width - vb-width * scale-x) to translate-x. - if (align.contains("xMax")) { - translateX += (eWidth - vbWidth * scaleX); - } + // If align contains 'yMid', add (e-height - vb-height * scale-y) / 2 to translate-y. + if (align.contains("YMid")) { + translateY += (eHeight - vbHeight * scaleY) / 2.0d; + } - // If align contains 'yMid', add (e-height - vb-height * scale-y) / 2 to translate-y. - if (align.contains("YMid")) { - translateY += (eHeight - vbHeight * scaleY) / 2.0d; - } - - // If align contains 'yMax', add (e-height - vb-height * scale-y) to translate-y. - if (align.contains("YMax")) { - translateY += (eHeight - vbHeight * scaleY); - } - - } - - // The transform applied to content contained by the element is given by - // translate(translate-x, translate-y) scale(scale-x, scale-y). - Matrix transform = new Matrix(); - transform.postTranslate((float) translateX, (float) translateY); - transform.preScale((float) scaleX, (float) scaleY); - return transform; + // If align contains 'yMax', add (e-height - vb-height * scale-y) to translate-y. + if (align.contains("YMax")) { + translateY += (eHeight - vbHeight * scaleY); + } } + + // The transform applied to content contained by the element is given by + // translate(translate-x, translate-y) scale(scale-x, scale-y). + Matrix transform = new Matrix(); + transform.postTranslate((float) translateX, (float) translateY); + transform.preScale((float) scaleX, (float) scaleY); + return transform; + } } diff --git a/android/src/main/java/com/horcrux/svg/VirtualView.java b/android/src/main/java/com/horcrux/svg/VirtualView.java index 99e36104..42f29c0f 100644 --- a/android/src/main/java/com/horcrux/svg/VirtualView.java +++ b/android/src/main/java/com/horcrux/svg/VirtualView.java @@ -1,5 +1,7 @@ package com.horcrux.svg; +import static com.horcrux.svg.FontData.DEFAULT_FONT_SIZE; + import android.annotation.SuppressLint; import android.graphics.Canvas; import android.graphics.Matrix; @@ -9,7 +11,6 @@ import android.graphics.RectF; import android.graphics.Region; import android.view.View; import android.view.ViewParent; - import com.facebook.common.logging.FLog; import com.facebook.react.bridge.Dynamic; import com.facebook.react.bridge.ReactContext; @@ -23,585 +24,580 @@ import com.facebook.react.uimanager.UIManagerModule; import com.facebook.react.uimanager.annotations.ReactProp; import com.facebook.react.uimanager.events.EventDispatcher; import com.facebook.react.views.view.ReactViewGroup; - import java.util.ArrayList; - import javax.annotation.Nullable; -import static com.horcrux.svg.FontData.DEFAULT_FONT_SIZE; - @SuppressLint("ViewConstructor") -abstract public class VirtualView extends ReactViewGroup { - final ReactContext mContext; +public abstract class VirtualView extends ReactViewGroup { + final ReactContext mContext; - VirtualView(ReactContext reactContext) { - super(reactContext); - mContext = reactContext; - mScale = DisplayMetricsHolder.getScreenDisplayMetrics().density; - } + VirtualView(ReactContext reactContext) { + super(reactContext); + mContext = reactContext; + mScale = DisplayMetricsHolder.getScreenDisplayMetrics().density; + } - /* - N[1/Sqrt[2], 36] - The inverse of the square root of 2. - Provide enough digits for the 128-bit IEEE quad (36 significant digits). - */ - private static final double M_SQRT1_2l = 0.707106781186547524400844362104849039; + /* + N[1/Sqrt[2], 36] + The inverse of the square root of 2. + Provide enough digits for the 128-bit IEEE quad (36 significant digits). + */ + private static final double M_SQRT1_2l = 0.707106781186547524400844362104849039; - private static final float[] sRawMatrix = new float[]{ + private static final float[] sRawMatrix = + new float[] { 1, 0, 0, 0, 1, 0, 0, 0, 1 - }; - float mOpacity = 1f; - Matrix mCTM = new Matrix(); - Matrix mMatrix = new Matrix(); - Matrix mTransform = new Matrix(); - Matrix mInvCTM = new Matrix(); - Matrix mInvMatrix = new Matrix(); - final Matrix mInvTransform = new Matrix(); - boolean mInvertible = true; - boolean mCTMInvertible = true; - boolean mTransformInvertible = true; - private RectF mClientRect; + }; + float mOpacity = 1f; + Matrix mCTM = new Matrix(); + Matrix mMatrix = new Matrix(); + Matrix mTransform = new Matrix(); + Matrix mInvCTM = new Matrix(); + Matrix mInvMatrix = new Matrix(); + final Matrix mInvTransform = new Matrix(); + boolean mInvertible = true; + boolean mCTMInvertible = true; + boolean mTransformInvertible = true; + private RectF mClientRect; - int mClipRule; - private @Nullable String mClipPath; - @Nullable String mMask; - @Nullable String mMarkerStart; - @Nullable String mMarkerMid; - @Nullable String mMarkerEnd; + int mClipRule; + private @Nullable String mClipPath; + @Nullable String mMask; + @Nullable String mMarkerStart; + @Nullable String mMarkerMid; + @Nullable String mMarkerEnd; - private static final int CLIP_RULE_EVENODD = 0; - static final int CLIP_RULE_NONZERO = 1; + private static final int CLIP_RULE_EVENODD = 0; + static final int CLIP_RULE_NONZERO = 1; - final float mScale; - private boolean mResponsible; - private boolean mOnLayout; - String mDisplay; - String mName; + final float mScale; + private boolean mResponsible; + private boolean mOnLayout; + String mDisplay; + String mName; - private SvgView svgView; - private Path mCachedClipPath; - private GroupView mTextRoot; - private double fontSize = -1; - private double canvasDiagonal = -1; - private float canvasHeight = -1; - private float canvasWidth = -1; - private GlyphContext glyphContext; + private SvgView svgView; + private Path mCachedClipPath; + private GroupView mTextRoot; + private double fontSize = -1; + private double canvasDiagonal = -1; + private float canvasHeight = -1; + private float canvasWidth = -1; + private GlyphContext glyphContext; - Path mPath; - Path mFillPath; - Path mStrokePath; - Path mMarkerPath; - Path mClipRegionPath; - RectF mBox; - RectF mFillBounds; - RectF mStrokeBounds; - RectF mMarkerBounds; - RectF mClipBounds; - Region mRegion; - Region mMarkerRegion; - Region mStrokeRegion; - Region mClipRegion; - ArrayList elements; - PointerEvents mPointerEvents; + Path mPath; + Path mFillPath; + Path mStrokePath; + Path mMarkerPath; + Path mClipRegionPath; + RectF mBox; + RectF mFillBounds; + RectF mStrokeBounds; + RectF mMarkerBounds; + RectF mClipBounds; + Region mRegion; + Region mMarkerRegion; + Region mStrokeRegion; + Region mClipRegion; + ArrayList elements; + PointerEvents mPointerEvents; - void setPointerEvents(PointerEvents pointerEvents) { - mPointerEvents = pointerEvents; + void setPointerEvents(PointerEvents pointerEvents) { + mPointerEvents = pointerEvents; + } + + @Override + public void invalidate() { + if (this instanceof RenderableView && mPath == null) { + return; } + clearCache(); + clearParentCache(); + super.invalidate(); + } - @Override - public void invalidate() { - if (this instanceof RenderableView && mPath == null) { - return; - } - clearCache(); - clearParentCache(); - super.invalidate(); + void clearCache() { + canvasDiagonal = -1; + canvasHeight = -1; + canvasWidth = -1; + fontSize = -1; + mStrokeRegion = null; + mMarkerRegion = null; + mRegion = null; + mPath = null; + } + + void clearChildCache() { + clearCache(); + for (int i = 0; i < getChildCount(); i++) { + View node = getChildAt(i); + if (node instanceof VirtualView) { + ((VirtualView) node).clearChildCache(); + } } + } - void clearCache() { - canvasDiagonal = -1; - canvasHeight = -1; - canvasWidth = -1; - fontSize = -1; - mStrokeRegion = null; - mMarkerRegion = null; - mRegion = null; - mPath = null; + private void clearParentCache() { + VirtualView node = this; + while (true) { + ViewParent parent = node.getParent(); + if (!(parent instanceof VirtualView)) { + return; + } + node = (VirtualView) parent; + if (node.mPath == null) { + return; + } + node.clearCache(); } + } - void clearChildCache() { - clearCache(); - for (int i = 0; i < getChildCount(); i++) { - View node = getChildAt(i); - if (node instanceof VirtualView) { - ((VirtualView)node).clearChildCache(); - } - } - } - - private void clearParentCache() { - VirtualView node = this; - while (true) { - ViewParent parent = node.getParent(); - if (!(parent instanceof VirtualView)) { - return; - } - node = (VirtualView)parent; - if (node.mPath == null) { - return; - } - node.clearCache(); - } - } - - @Nullable - GroupView getTextRoot() { - VirtualView node = this; - if (mTextRoot == null) { - while (node != null) { - if (node instanceof GroupView && ((GroupView) node).getGlyphContext() != null) { - mTextRoot = (GroupView)node; - break; - } - - ViewParent parent = node.getParent(); - - if (!(parent instanceof VirtualView)) { - node = null; - } else { - node = (VirtualView)parent; - } - } + @Nullable + GroupView getTextRoot() { + VirtualView node = this; + if (mTextRoot == null) { + while (node != null) { + if (node instanceof GroupView && ((GroupView) node).getGlyphContext() != null) { + mTextRoot = (GroupView) node; + break; } - return mTextRoot; - } + ViewParent parent = node.getParent(); - @Nullable - GroupView getParentTextRoot() { - ViewParent parent = this.getParent(); if (!(parent instanceof VirtualView)) { - return null; + node = null; } else { - return ((VirtualView) parent).getTextRoot(); + node = (VirtualView) parent; } + } } + return mTextRoot; + } - private double getFontSizeFromContext() { - if (fontSize != -1) { - return fontSize; - } - GroupView root = getTextRoot(); - if (root == null) { - return DEFAULT_FONT_SIZE; - } + @Nullable + GroupView getParentTextRoot() { + ViewParent parent = this.getParent(); + if (!(parent instanceof VirtualView)) { + return null; + } else { + return ((VirtualView) parent).getTextRoot(); + } + } - if (glyphContext == null) { - glyphContext = root.getGlyphContext(); - } - - fontSize = glyphContext.getFontSize(); - - return fontSize; + private double getFontSizeFromContext() { + if (fontSize != -1) { + return fontSize; + } + GroupView root = getTextRoot(); + if (root == null) { + return DEFAULT_FONT_SIZE; } - abstract void draw(Canvas canvas, Paint paint, float opacity); - void render(Canvas canvas, Paint paint, float opacity) { - draw(canvas, paint, opacity); + if (glyphContext == null) { + glyphContext = root.getGlyphContext(); } - /** - * Sets up the transform matrix on the canvas before an element is drawn. - * - * NB: for perf reasons this does not apply opacity, as that would mean creating a new canvas - * layer (which allocates an offscreen bitmap) and having it composited afterwards. Instead, the - * drawing code should apply opacity recursively. - * - * @param canvas the canvas to set up - * @param ctm current transformation matrix - */ - int saveAndSetupCanvas(Canvas canvas, Matrix ctm) { - int count = canvas.save(); - mCTM.setConcat(mMatrix, mTransform); - canvas.concat(mCTM); - mCTM.preConcat(ctm); - mCTMInvertible = mCTM.invert(mInvCTM); - return count; - } + fontSize = glyphContext.getFontSize(); - /** - * Restore the canvas after an element was drawn. This is always called in mirror with - * {@link #saveAndSetupCanvas}. - * - * @param canvas the canvas to restore - */ - void restoreCanvas(Canvas canvas, int count) { - canvas.restoreToCount(count); - } + return fontSize; + } - @ReactProp(name = "name") - public void setName(String name) { - mName = name; - invalidate(); - } + abstract void draw(Canvas canvas, Paint paint, float opacity); - @ReactProp(name = "display") - public void setDisplay(String display) { - mDisplay = display; - invalidate(); - } + void render(Canvas canvas, Paint paint, float opacity) { + draw(canvas, paint, opacity); + } - @ReactProp(name = "onLayout") - public void setOnLayout(boolean onLayout) { - mOnLayout = onLayout; - invalidate(); - } + /** + * Sets up the transform matrix on the canvas before an element is drawn. + * + *

NB: for perf reasons this does not apply opacity, as that would mean creating a new canvas + * layer (which allocates an offscreen bitmap) and having it composited afterwards. Instead, the + * drawing code should apply opacity recursively. + * + * @param canvas the canvas to set up + * @param ctm current transformation matrix + */ + int saveAndSetupCanvas(Canvas canvas, Matrix ctm) { + int count = canvas.save(); + mCTM.setConcat(mMatrix, mTransform); + canvas.concat(mCTM); + mCTM.preConcat(ctm); + mCTMInvertible = mCTM.invert(mInvCTM); + return count; + } - @ReactProp(name = "mask") - public void setMask(String mask) { - mMask = mask; - invalidate(); - } + /** + * Restore the canvas after an element was drawn. This is always called in mirror with {@link + * #saveAndSetupCanvas}. + * + * @param canvas the canvas to restore + */ + void restoreCanvas(Canvas canvas, int count) { + canvas.restoreToCount(count); + } - @ReactProp(name = "markerStart") - public void setMarkerStart(String markerStart) { - mMarkerStart = markerStart; - invalidate(); - } + @ReactProp(name = "name") + public void setName(String name) { + mName = name; + invalidate(); + } - @ReactProp(name = "markerMid") - public void setMarkerMid(String markerMid) { - mMarkerMid = markerMid; - invalidate(); - } + @ReactProp(name = "display") + public void setDisplay(String display) { + mDisplay = display; + invalidate(); + } - @ReactProp(name = "markerEnd") - public void setMarkerEnd(String markerEnd) { - mMarkerEnd = markerEnd; - invalidate(); - } + @ReactProp(name = "onLayout") + public void setOnLayout(boolean onLayout) { + mOnLayout = onLayout; + invalidate(); + } - @ReactProp(name = "clipPath") - public void setClipPath(String clipPath) { - mCachedClipPath = null; - mClipPath = clipPath; - invalidate(); - } + @ReactProp(name = "mask") + public void setMask(String mask) { + mMask = mask; + invalidate(); + } - @ReactProp(name = "clipRule", defaultInt = CLIP_RULE_NONZERO) - public void setClipRule(int clipRule) { - mClipRule = clipRule; - invalidate(); - } + @ReactProp(name = "markerStart") + public void setMarkerStart(String markerStart) { + mMarkerStart = markerStart; + invalidate(); + } - @ReactProp(name = "opacity", defaultFloat = 1f) - public void setOpacity(float opacity) { - mOpacity = opacity; - invalidate(); - } + @ReactProp(name = "markerMid") + public void setMarkerMid(String markerMid) { + mMarkerMid = markerMid; + invalidate(); + } - @ReactProp(name = "matrix") - public void setMatrix(Dynamic matrixArray) { - ReadableType type = matrixArray.getType(); - if (!matrixArray.isNull() && type.equals(ReadableType.Array)) { - setMatrix((ReadableArray) matrixArray); - } else { - mMatrix.reset(); - mInvMatrix.reset(); - mInvertible = true; - super.invalidate(); - clearParentCache(); - } + @ReactProp(name = "markerEnd") + public void setMarkerEnd(String markerEnd) { + mMarkerEnd = markerEnd; + invalidate(); + } + + @ReactProp(name = "clipPath") + public void setClipPath(String clipPath) { + mCachedClipPath = null; + mClipPath = clipPath; + invalidate(); + } + + @ReactProp(name = "clipRule", defaultInt = CLIP_RULE_NONZERO) + public void setClipRule(int clipRule) { + mClipRule = clipRule; + invalidate(); + } + + @ReactProp(name = "opacity", defaultFloat = 1f) + public void setOpacity(float opacity) { + mOpacity = opacity; + invalidate(); + } + + @ReactProp(name = "matrix") + public void setMatrix(Dynamic matrixArray) { + ReadableType type = matrixArray.getType(); + if (!matrixArray.isNull() && type.equals(ReadableType.Array)) { + setMatrix((ReadableArray) matrixArray); + } else { + mMatrix.reset(); + mInvMatrix.reset(); + mInvertible = true; + super.invalidate(); + clearParentCache(); } + } public void setMatrix(ReadableArray matrixArray) { - int matrixSize = PropHelper.toMatrixData(matrixArray, sRawMatrix, mScale); - if (matrixSize == 6) { - if (mMatrix == null) { - mMatrix = new Matrix(); - mInvMatrix = new Matrix(); - } - mMatrix.setValues(sRawMatrix); - mInvertible = mMatrix.invert(mInvMatrix); - } else if (matrixSize != -1) { - FLog.w(ReactConstants.TAG, "RNSVG: Transform matrices must be of size 6"); + int matrixSize = PropHelper.toMatrixData(matrixArray, sRawMatrix, mScale); + if (matrixSize == 6) { + if (mMatrix == null) { + mMatrix = new Matrix(); + mInvMatrix = new Matrix(); } + mMatrix.setValues(sRawMatrix); + mInvertible = mMatrix.invert(mInvMatrix); + } else if (matrixSize != -1) { + FLog.w(ReactConstants.TAG, "RNSVG: Transform matrices must be of size 6"); + } super.invalidate(); clearParentCache(); } - @ReactProp(name = "responsible") - public void setResponsible(boolean responsible) { - mResponsible = responsible; - invalidate(); + @ReactProp(name = "responsible") + public void setResponsible(boolean responsible) { + mResponsible = responsible; + invalidate(); + } + + @Nullable + Path getClipPath() { + return mCachedClipPath; + } + + @Nullable + Path getClipPath(Canvas canvas, Paint paint) { + if (mClipPath != null) { + ClipPathView mClipNode = (ClipPathView) getSvgView().getDefinedClipPath(mClipPath); + + if (mClipNode != null) { + Path clipPath = + mClipRule == CLIP_RULE_EVENODD + ? mClipNode.getPath(canvas, paint) + : mClipNode.getPath(canvas, paint, Region.Op.UNION); + clipPath.transform(mClipNode.mMatrix); + clipPath.transform(mClipNode.mTransform); + switch (mClipRule) { + case CLIP_RULE_EVENODD: + clipPath.setFillType(Path.FillType.EVEN_ODD); + break; + case CLIP_RULE_NONZERO: + break; + default: + FLog.w(ReactConstants.TAG, "RNSVG: clipRule: " + mClipRule + " unrecognized"); + } + mCachedClipPath = clipPath; + } else { + FLog.w(ReactConstants.TAG, "RNSVG: Undefined clipPath: " + mClipPath); + } } - @Nullable Path getClipPath() { - return mCachedClipPath; + return getClipPath(); + } + + void clip(Canvas canvas, Paint paint) { + Path clip = getClipPath(canvas, paint); + + if (clip != null) { + canvas.clipPath(clip); + } + } + + abstract int hitTest(final float[] point); + + boolean isResponsible() { + return mResponsible; + } + + abstract Path getPath(Canvas canvas, Paint paint); + + SvgView getSvgView() { + if (svgView != null) { + return svgView; } - @Nullable Path getClipPath(Canvas canvas, Paint paint) { - if (mClipPath != null) { - ClipPathView mClipNode = (ClipPathView) getSvgView().getDefinedClipPath(mClipPath); + ViewParent parent = getParent(); - if (mClipNode != null) { - Path clipPath = mClipRule == CLIP_RULE_EVENODD ? mClipNode.getPath(canvas, paint) : - mClipNode.getPath(canvas, paint, Region.Op.UNION); - clipPath.transform(mClipNode.mMatrix); - clipPath.transform(mClipNode.mTransform); - switch (mClipRule) { - case CLIP_RULE_EVENODD: - clipPath.setFillType(Path.FillType.EVEN_ODD); - break; - case CLIP_RULE_NONZERO: - break; - default: - FLog.w(ReactConstants.TAG, "RNSVG: clipRule: " + mClipRule + " unrecognized"); - } - mCachedClipPath = clipPath; - } else { - FLog.w(ReactConstants.TAG, "RNSVG: Undefined clipPath: " + mClipPath); - } - } - - return getClipPath(); + if (parent == null) { + return null; + } else if (parent instanceof SvgView) { + svgView = (SvgView) parent; + } else if (parent instanceof VirtualView) { + svgView = ((VirtualView) parent).getSvgView(); + } else { + FLog.e( + ReactConstants.TAG, + "RNSVG: " + getClass().getName() + " should be descendant of a SvgView."); } - void clip(Canvas canvas, Paint paint) { - Path clip = getClipPath(canvas, paint); + return svgView; + } - if (clip != null) { - canvas.clipPath(clip); - } + double relativeOnWidth(SVGLength length) { + SVGLength.UnitType unit = length.unit; + if (unit == SVGLength.UnitType.NUMBER) { + return length.value * mScale; + } else if (unit == SVGLength.UnitType.PERCENTAGE) { + return length.value / 100 * getCanvasWidth(); + } + return fromRelativeFast(length); + } + + double relativeOnHeight(SVGLength length) { + SVGLength.UnitType unit = length.unit; + if (unit == SVGLength.UnitType.NUMBER) { + return length.value * mScale; + } else if (unit == SVGLength.UnitType.PERCENTAGE) { + return length.value / 100 * getCanvasHeight(); + } + return fromRelativeFast(length); + } + + double relativeOnOther(SVGLength length) { + SVGLength.UnitType unit = length.unit; + if (unit == SVGLength.UnitType.NUMBER) { + return length.value * mScale; + } else if (unit == SVGLength.UnitType.PERCENTAGE) { + return length.value / 100 * getCanvasDiagonal(); + } + return fromRelativeFast(length); + } + + /** + * Converts SVGLength into px / user units in the current user coordinate system + * + * @param length length string + * @return value in the current user coordinate system + */ + private double fromRelativeFast(SVGLength length) { + double unit; + switch (length.unit) { + case EMS: + unit = getFontSizeFromContext(); + break; + case EXS: + unit = getFontSizeFromContext() / 2; + break; + + case CM: + unit = 35.43307; + break; + case MM: + unit = 3.543307; + break; + case IN: + unit = 90; + break; + case PT: + unit = 1.25; + break; + case PC: + unit = 15; + break; + + default: + unit = 1; + } + return length.value * unit * mScale; + } + + private float getCanvasWidth() { + if (canvasWidth != -1) { + return canvasWidth; + } + GroupView root = getTextRoot(); + if (root == null) { + canvasWidth = getSvgView().getCanvasBounds().width(); + } else { + canvasWidth = root.getGlyphContext().getWidth(); } - abstract int hitTest(final float[] point); + return canvasWidth; + } - boolean isResponsible() { - return mResponsible; + private float getCanvasHeight() { + if (canvasHeight != -1) { + return canvasHeight; + } + GroupView root = getTextRoot(); + if (root == null) { + canvasHeight = getSvgView().getCanvasBounds().height(); + } else { + canvasHeight = root.getGlyphContext().getHeight(); } - abstract Path getPath(Canvas canvas, Paint paint); + return canvasHeight; + } - SvgView getSvgView() { - if (svgView != null) { - return svgView; - } + private double getCanvasDiagonal() { + if (canvasDiagonal != -1) { + return canvasDiagonal; + } + double powX = Math.pow((getCanvasWidth()), 2); + double powY = Math.pow((getCanvasHeight()), 2); + canvasDiagonal = Math.sqrt(powX + powY) * M_SQRT1_2l; + return canvasDiagonal; + } - ViewParent parent = getParent(); + void saveDefinition() { + if (mName != null) { + getSvgView().defineTemplate(this, mName); + } + } - if (parent == null) { - return null; - } else if (parent instanceof SvgView) { - svgView = (SvgView)parent; - } else if (parent instanceof VirtualView) { - svgView = ((VirtualView) parent).getSvgView(); - } else { - FLog.e(ReactConstants.TAG, "RNSVG: " + getClass().getName() + " should be descendant of a SvgView."); - } + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int width = + mClientRect != null + ? (int) Math.ceil(mClientRect.width()) + : getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec); - return svgView; + int height = + mClientRect != null + ? (int) Math.ceil(mClientRect.height()) + : getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec); + + setMeasuredDimension(width, height); + } + + /** + * Called from layout when this view should assign a size and position to each of its children. + * + *

Derived classes with children should override this method and call layout on each of their + * children. + * + * @param changed This is a new size or position for this view + * @param pleft Left position, relative to parent + * @param ptop Top position, relative to parent + * @param pright Right position, relative to parent + * @param pbottom Bottom position, relative to parent + */ + protected void onLayout(boolean changed, int pleft, int ptop, int pright, int pbottom) { + if (mClientRect == null) { + return; } - double relativeOnWidth(SVGLength length) { - SVGLength.UnitType unit = length.unit; - if (unit == SVGLength.UnitType.NUMBER){ - return length.value * mScale; - } else if (unit == SVGLength.UnitType.PERCENTAGE){ - return length.value / 100 * getCanvasWidth(); - } - return fromRelativeFast(length); + if (!(this instanceof GroupView)) { + int left = (int) Math.floor(mClientRect.left); + int top = (int) Math.floor(mClientRect.top); + int right = (int) Math.ceil(mClientRect.right); + int bottom = (int) Math.ceil(mClientRect.bottom); + setLeft(left); + setTop(top); + setRight(right); + setBottom(bottom); } + int width = (int) Math.ceil(mClientRect.width()); + int height = (int) Math.ceil(mClientRect.height()); + setMeasuredDimension(width, height); + } - double relativeOnHeight(SVGLength length) { - SVGLength.UnitType unit = length.unit; - if (unit == SVGLength.UnitType.NUMBER){ - return length.value * mScale; - } else if (unit == SVGLength.UnitType.PERCENTAGE){ - return length.value / 100 * getCanvasHeight(); - } - return fromRelativeFast(length); + void setClientRect(RectF rect) { + if (mClientRect != null && mClientRect.equals(rect)) { + return; } - - double relativeOnOther(SVGLength length) { - SVGLength.UnitType unit = length.unit; - if (unit == SVGLength.UnitType.NUMBER){ - return length.value * mScale; - } else if (unit == SVGLength.UnitType.PERCENTAGE){ - return length.value / 100 * getCanvasDiagonal(); - } - return fromRelativeFast(length); + mClientRect = rect; + if (mClientRect == null) { + return; } - - /** - * Converts SVGLength into px / user units - * in the current user coordinate system - * - * @param length length string - * @return value in the current user coordinate system - */ - private double fromRelativeFast(SVGLength length) { - double unit; - switch (length.unit) { - case EMS: - unit = getFontSizeFromContext(); - break; - case EXS: - unit = getFontSizeFromContext() / 2; - break; - - case CM: - unit = 35.43307; - break; - case MM: - unit = 3.543307; - break; - case IN: - unit = 90; - break; - case PT: - unit = 1.25; - break; - case PC: - unit = 15; - break; - - default: - unit = 1; - } - return length.value * unit * mScale; + int width = (int) Math.ceil(mClientRect.width()); + int height = (int) Math.ceil(mClientRect.height()); + int left = (int) Math.floor(mClientRect.left); + int top = (int) Math.floor(mClientRect.top); + int right = (int) Math.ceil(mClientRect.right); + int bottom = (int) Math.ceil(mClientRect.bottom); + setMeasuredDimension(width, height); + if (!(this instanceof GroupView)) { + setLeft(left); + setTop(top); + setRight(right); + setBottom(bottom); } - - private float getCanvasWidth() { - if (canvasWidth != -1) { - return canvasWidth; - } - GroupView root = getTextRoot(); - if (root == null) { - canvasWidth = getSvgView().getCanvasBounds().width(); - } else { - canvasWidth = root.getGlyphContext().getWidth(); - } - - return canvasWidth; - } - - private float getCanvasHeight() { - if (canvasHeight != -1) { - return canvasHeight; - } - GroupView root = getTextRoot(); - if (root == null) { - canvasHeight = getSvgView().getCanvasBounds().height(); - } else { - canvasHeight = root.getGlyphContext().getHeight(); - } - - return canvasHeight; - } - - private double getCanvasDiagonal() { - if (canvasDiagonal != -1) { - return canvasDiagonal; - } - double powX = Math.pow((getCanvasWidth()), 2); - double powY = Math.pow((getCanvasHeight()), 2); - canvasDiagonal = Math.sqrt(powX + powY) * M_SQRT1_2l; - return canvasDiagonal; - } - - void saveDefinition() { - if (mName != null) { - getSvgView().defineTemplate(this, mName); - } - } - - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - int width = mClientRect != null ? - (int) Math.ceil(mClientRect.width()) - : getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec); - - int height = mClientRect != null ? - (int) Math.ceil(mClientRect.height()) - : getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec); - - setMeasuredDimension(width, height); - } - - /** - * Called from layout when this view should - * assign a size and position to each of its children. - * - * Derived classes with children should override - * this method and call layout on each of - * their children. - * @param changed This is a new size or position for this view - * @param pleft Left position, relative to parent - * @param ptop Top position, relative to parent - * @param pright Right position, relative to parent - * @param pbottom Bottom position, relative to parent - */ - protected void onLayout(boolean changed, int pleft, int ptop, int pright, int pbottom) { - if (mClientRect == null) { - return; - } - - if (!(this instanceof GroupView)) { - int left = (int) Math.floor(mClientRect.left); - int top = (int) Math.floor(mClientRect.top); - int right = (int) Math.ceil(mClientRect.right); - int bottom = (int) Math.ceil(mClientRect.bottom); - setLeft(left); - setTop(top); - setRight(right); - setBottom(bottom); - } - int width = (int) Math.ceil(mClientRect.width()); - int height = (int) Math.ceil(mClientRect.height()); - setMeasuredDimension(width, height); - } - - void setClientRect(RectF rect) { - if (mClientRect != null && mClientRect.equals(rect)) { - return; - } - mClientRect = rect; - if (mClientRect == null) { - return; - } - int width = (int) Math.ceil(mClientRect.width()); - int height = (int) Math.ceil(mClientRect.height()); - int left = (int) Math.floor(mClientRect.left); - int top = (int) Math.floor(mClientRect.top); - int right = (int) Math.ceil(mClientRect.right); - int bottom = (int) Math.ceil(mClientRect.bottom); - setMeasuredDimension(width, height); - if (!(this instanceof GroupView)) { - setLeft(left); - setTop(top); - setRight(right); - setBottom(bottom); - } - if (!mOnLayout) { - return; - } - EventDispatcher eventDispatcher = mContext - .getNativeModule(UIManagerModule.class) - .getEventDispatcher(); - eventDispatcher.dispatchEvent(OnLayoutEvent.obtain( - this.getId(), - left, - top, - width, - height - )); - } - - RectF getClientRect() { - return mClientRect; + if (!mOnLayout) { + return; } + EventDispatcher eventDispatcher = + mContext.getNativeModule(UIManagerModule.class).getEventDispatcher(); + eventDispatcher.dispatchEvent(OnLayoutEvent.obtain(this.getId(), left, top, width, height)); + } + RectF getClientRect() { + return mClientRect; + } } diff --git a/android/src/paper/java/com/horcrux/svg/FabricEnabledViewGroup.java b/android/src/paper/java/com/horcrux/svg/FabricEnabledViewGroup.java index b3f40f9b..08ab8605 100644 --- a/android/src/paper/java/com/horcrux/svg/FabricEnabledViewGroup.java +++ b/android/src/paper/java/com/horcrux/svg/FabricEnabledViewGroup.java @@ -1,9 +1,7 @@ package com.horcrux.svg; - import com.facebook.react.bridge.ReactContext; import com.facebook.react.views.view.ReactViewGroup; - import org.jetbrains.annotations.Nullable; public abstract class FabricEnabledViewGroup extends ReactViewGroup { diff --git a/apple/Brushes/RNSVGBrush.h b/apple/Brushes/RNSVGBrush.h index edf3fc40..4df3f1b6 100644 --- a/apple/Brushes/RNSVGBrush.h +++ b/apple/Brushes/RNSVGBrush.h @@ -12,7 +12,7 @@ @interface RNSVGBrush : NSObject -@property (nonatomic, strong) NSString* brushRef; +@property (nonatomic, strong) NSString *brushRef; /* @abstract */ - (instancetype)initWithArray:(NSArray *)data; diff --git a/apple/Brushes/RNSVGBrush.mm b/apple/Brushes/RNSVGBrush.mm index e7c8ae66..16905be9 100644 --- a/apple/Brushes/RNSVGBrush.mm +++ b/apple/Brushes/RNSVGBrush.mm @@ -13,31 +13,30 @@ - (instancetype)initWithArray:(NSArray *)data { - return [super init]; + return [super init]; } - (void)paint:(CGContextRef)context opacity:(CGFloat)opacity painter:(RNSVGPainter *)painter bounds:(CGRect)bounds { - } - (BOOL)applyFillColor:(CGContextRef)context opacity:(CGFloat)opacity { - return NO; + return NO; } - (BOOL)applyStrokeColor:(CGContextRef)context opacity:(CGFloat)opacity { - return NO; + return NO; } - (CGColorRef)getColorWithOpacity:(CGFloat)opacity { - return nil; + return nil; } - (void)paint:(CGContextRef)context opacity:(CGFloat)opacity painter:(RNSVGPainter *)painter { - // abstract + // abstract } @end diff --git a/apple/Brushes/RNSVGBrushType.h b/apple/Brushes/RNSVGBrushType.h index 3cc4a303..19ade8d5 100644 --- a/apple/Brushes/RNSVGBrushType.h +++ b/apple/Brushes/RNSVGBrushType.h @@ -7,8 +7,8 @@ */ typedef enum { - kRNSVGUndefinedType, - kRNSVGLinearGradient, - kRNSVGRadialGradient, - kRNSVGPattern + kRNSVGUndefinedType, + kRNSVGLinearGradient, + kRNSVGRadialGradient, + kRNSVGPattern } RNSVGBrushType; diff --git a/apple/Brushes/RNSVGContextBrush.mm b/apple/Brushes/RNSVGContextBrush.mm index 43d7c2af..874fe391 100644 --- a/apple/Brushes/RNSVGContextBrush.mm +++ b/apple/Brushes/RNSVGContextBrush.mm @@ -7,31 +7,30 @@ */ #import "RNSVGContextBrush.h" -#import "RNSVGRenderable.h" #import "RNSVGNode.h" +#import "RNSVGRenderable.h" -#import "RCTConvert+RNSVG.h" #import +#import "RCTConvert+RNSVG.h" -@implementation RNSVGContextBrush -{ - BOOL _isStroke; +@implementation RNSVGContextBrush { + BOOL _isStroke; } - (instancetype)initFill { - if ((self = [super initWithArray:nil])) { - _isStroke = NO; - } - return self; + if ((self = [super initWithArray:nil])) { + _isStroke = NO; + } + return self; } - (instancetype)initStroke { - if ((self = [super initWithArray:nil])) { - _isStroke = YES; - } - return self; + if ((self = [super initWithArray:nil])) { + _isStroke = YES; + } + return self; } - (void)dealloc @@ -40,46 +39,44 @@ - (BOOL)applyFillColor:(CGContextRef)context opacity:(CGFloat)opacity { - RNSVGRenderable *element = RNSVGRenderable.contextElement; - if (!element) { - return NO; - } + RNSVGRenderable *element = RNSVGRenderable.contextElement; + if (!element) { + return NO; + } - RNSVGBrush *brush = _isStroke ? element.stroke : element.fill; + RNSVGBrush *brush = _isStroke ? element.stroke : element.fill; - BOOL fillColor; + BOOL fillColor; - if (brush.class == RNSVGBrush.class) { - CGContextSetFillColorWithColor(context, [element.tintColor CGColor]); - fillColor = YES; - } else { - fillColor = [brush applyFillColor:context opacity:opacity]; - } + if (brush.class == RNSVGBrush.class) { + CGContextSetFillColorWithColor(context, [element.tintColor CGColor]); + fillColor = YES; + } else { + fillColor = [brush applyFillColor:context opacity:opacity]; + } - return fillColor; + return fillColor; } - - - (BOOL)applyStrokeColor:(CGContextRef)context opacity:(CGFloat)opacity { - RNSVGRenderable *element = RNSVGRenderable.contextElement; - if (!element) { - return NO; - } + RNSVGRenderable *element = RNSVGRenderable.contextElement; + if (!element) { + return NO; + } - RNSVGBrush *brush = _isStroke ? element.stroke : element.fill; + RNSVGBrush *brush = _isStroke ? element.stroke : element.fill; - BOOL strokeColor; + BOOL strokeColor; - if (brush.class == RNSVGBrush.class) { - CGContextSetStrokeColorWithColor(context, [element.tintColor CGColor]); - strokeColor = YES; - } else { - strokeColor = [brush applyStrokeColor:context opacity:opacity]; - } + if (brush.class == RNSVGBrush.class) { + CGContextSetStrokeColorWithColor(context, [element.tintColor CGColor]); + strokeColor = YES; + } else { + strokeColor = [brush applyStrokeColor:context opacity:opacity]; + } - return YES; + return YES; } @end diff --git a/apple/Brushes/RNSVGPainter.h b/apple/Brushes/RNSVGPainter.h index f7e73853..cd5519fa 100644 --- a/apple/Brushes/RNSVGPainter.h +++ b/apple/Brushes/RNSVGPainter.h @@ -8,14 +8,14 @@ #import "RCTConvert+RNSVG.h" #import "RNSVGBrushType.h" -#import "RNSVGUnits.h" #import "RNSVGLength.h" +#import "RNSVGUnits.h" @class RNSVGPattern; @interface RNSVGPainter : NSObject -@property (nonatomic, assign) RNSVGPattern* pattern; +@property (nonatomic, assign) RNSVGPattern *pattern; @property (nonatomic, assign) CGRect paintBounds; @property (nonatomic, assign) bool useObjectBoundingBoxForContentUnits; @property (nonatomic, assign) CGRect bounds; diff --git a/apple/Brushes/RNSVGPainter.mm b/apple/Brushes/RNSVGPainter.mm index 48539b7e..276fa6e3 100644 --- a/apple/Brushes/RNSVGPainter.mm +++ b/apple/Brushes/RNSVGPainter.mm @@ -10,246 +10,242 @@ #import "RNSVGPattern.h" #import "RNSVGViewBox.h" -@implementation RNSVGPainter -{ - NSArray *_points; - NSArray *_colors; - RNSVGBrushType _type; - BOOL _useObjectBoundingBox; - BOOL _useContentObjectBoundingBox; - CGAffineTransform _transform; - CGRect _userSpaceBoundingBox; +@implementation RNSVGPainter { + NSArray *_points; + NSArray *_colors; + RNSVGBrushType _type; + BOOL _useObjectBoundingBox; + BOOL _useContentObjectBoundingBox; + CGAffineTransform _transform; + CGRect _userSpaceBoundingBox; } - (instancetype)initWithPointsArray:(NSArray *)pointsArray { - if ((self = [super init])) { - _points = pointsArray; - } - return self; + if ((self = [super init])) { + _points = pointsArray; + } + return self; } -RCT_NOT_IMPLEMENTED(- (instancetype)init) +RCT_NOT_IMPLEMENTED(-(instancetype)init) - (void)setUnits:(RNSVGUnits)unit { - _useObjectBoundingBox = unit == kRNSVGUnitsObjectBoundingBox; + _useObjectBoundingBox = unit == kRNSVGUnitsObjectBoundingBox; } - (void)setContentUnits:(RNSVGUnits)unit { - _useContentObjectBoundingBox = unit == kRNSVGUnitsObjectBoundingBox; + _useContentObjectBoundingBox = unit == kRNSVGUnitsObjectBoundingBox; } - (void)setUserSpaceBoundingBox:(CGRect)userSpaceBoundingBox { - _userSpaceBoundingBox = userSpaceBoundingBox; + _userSpaceBoundingBox = userSpaceBoundingBox; } - (void)setTransform:(CGAffineTransform)transform { - _transform = transform; + _transform = transform; } - (void)setPattern:(RNSVGPattern *)pattern { - if (_type != kRNSVGUndefinedType) { - // todo: throw error - return; - } + if (_type != kRNSVGUndefinedType) { + // todo: throw error + return; + } - _type = kRNSVGPattern; - _pattern = pattern; + _type = kRNSVGPattern; + _pattern = pattern; } - (void)setLinearGradientColors:(NSArray *)colors { - if (_type != kRNSVGUndefinedType) { - // todo: throw error - return; - } + if (_type != kRNSVGUndefinedType) { + // todo: throw error + return; + } - _type = kRNSVGLinearGradient; - _colors = colors; + _type = kRNSVGLinearGradient; + _colors = colors; } - (void)setRadialGradientColors:(NSArray *)colors { - if (_type != kRNSVGUndefinedType) { - // todo: throw error - return; - } + if (_type != kRNSVGUndefinedType) { + // todo: throw error + return; + } - _type = kRNSVGRadialGradient; - _colors = colors; + _type = kRNSVGRadialGradient; + _colors = colors; } - (void)paint:(CGContextRef)context bounds:(CGRect)bounds { - if (_type == kRNSVGLinearGradient) { - [self paintLinearGradient:context bounds:bounds]; - } else if (_type == kRNSVGRadialGradient) { - [self paintRadialGradient:context bounds:bounds]; - } else if (_type == kRNSVGPattern) { - [self paintPattern:context bounds:bounds]; - } + if (_type == kRNSVGLinearGradient) { + [self paintLinearGradient:context bounds:bounds]; + } else if (_type == kRNSVGRadialGradient) { + [self paintRadialGradient:context bounds:bounds]; + } else if (_type == kRNSVGPattern) { + [self paintPattern:context bounds:bounds]; + } } - (CGRect)getPaintRect:(CGContextRef)context bounds:(CGRect)bounds { - CGRect rect = _useObjectBoundingBox ? bounds : _userSpaceBoundingBox; - CGFloat height = CGRectGetHeight(rect); - CGFloat width = CGRectGetWidth(rect); - CGFloat x = 0.0; - CGFloat y = 0.0; + CGRect rect = _useObjectBoundingBox ? bounds : _userSpaceBoundingBox; + CGFloat height = CGRectGetHeight(rect); + CGFloat width = CGRectGetWidth(rect); + CGFloat x = 0.0; + CGFloat y = 0.0; - if (_useObjectBoundingBox) { - x = CGRectGetMinX(rect); - y = CGRectGetMinY(rect); - } + if (_useObjectBoundingBox) { + x = CGRectGetMinX(rect); + y = CGRectGetMinY(rect); + } - return CGRectMake(x, y, width, height); + return CGRectMake(x, y, width, height); } -void PatternFunction(void* info, CGContextRef context) +void PatternFunction(void *info, CGContextRef context) { - RNSVGPainter *_painter = (__bridge RNSVGPainter *)info; - RNSVGPattern *_pattern = [_painter pattern]; - CGRect rect = _painter.paintBounds; - CGFloat minX = _pattern.minX; - CGFloat minY = _pattern.minY; - CGFloat vbWidth = _pattern.vbWidth; - CGFloat vbHeight = _pattern.vbHeight; - if (vbWidth > 0 && vbHeight > 0) { - CGRect vbRect = CGRectMake(minX, minY, vbWidth, vbHeight); - CGAffineTransform _viewBoxTransform = [RNSVGViewBox - getTransform:vbRect - eRect:rect - align:_pattern.align - meetOrSlice:_pattern.meetOrSlice]; - CGContextConcatCTM(context, _viewBoxTransform); - } + RNSVGPainter *_painter = (__bridge RNSVGPainter *)info; + RNSVGPattern *_pattern = [_painter pattern]; + CGRect rect = _painter.paintBounds; + CGFloat minX = _pattern.minX; + CGFloat minY = _pattern.minY; + CGFloat vbWidth = _pattern.vbWidth; + CGFloat vbHeight = _pattern.vbHeight; + if (vbWidth > 0 && vbHeight > 0) { + CGRect vbRect = CGRectMake(minX, minY, vbWidth, vbHeight); + CGAffineTransform _viewBoxTransform = [RNSVGViewBox getTransform:vbRect + eRect:rect + align:_pattern.align + meetOrSlice:_pattern.meetOrSlice]; + CGContextConcatCTM(context, _viewBoxTransform); + } - if (_painter.useObjectBoundingBoxForContentUnits) { - CGRect bounds = _painter.bounds; - CGContextConcatCTM(context, CGAffineTransformMakeScale(bounds.size.width, bounds.size.height)); - } + if (_painter.useObjectBoundingBoxForContentUnits) { + CGRect bounds = _painter.bounds; + CGContextConcatCTM(context, CGAffineTransformMakeScale(bounds.size.width, bounds.size.height)); + } - [_pattern renderTo:context rect:rect]; + [_pattern renderTo:context rect:rect]; } -- (CGFloat)getVal:(RNSVGLength*)length relative:(CGFloat)relative +- (CGFloat)getVal:(RNSVGLength *)length relative:(CGFloat)relative { - RNSVGLengthUnitType unit = [length unit]; - CGFloat val = [RNSVGPropHelper fromRelative:length - relative:relative]; - return _useObjectBoundingBox && - unit == SVG_LENGTHTYPE_NUMBER ? val * relative : val; + RNSVGLengthUnitType unit = [length unit]; + CGFloat val = [RNSVGPropHelper fromRelative:length relative:relative]; + return _useObjectBoundingBox && unit == SVG_LENGTHTYPE_NUMBER ? val * relative : val; } - (void)paintPattern:(CGContextRef)context bounds:(CGRect)bounds { - CGRect rect = [self getPaintRect:context bounds:bounds]; - CGFloat height = CGRectGetHeight(rect); - CGFloat width = CGRectGetWidth(rect); + CGRect rect = [self getPaintRect:context bounds:bounds]; + CGFloat height = CGRectGetHeight(rect); + CGFloat width = CGRectGetWidth(rect); - CGFloat x = [self getVal:[_points objectAtIndex:0] relative:width]; - CGFloat y = [self getVal:[_points objectAtIndex:1] relative:height]; - CGFloat w = [self getVal:[_points objectAtIndex:2] relative:width]; - CGFloat h = [self getVal:[_points objectAtIndex:3] relative:height]; + CGFloat x = [self getVal:[_points objectAtIndex:0] relative:width]; + CGFloat y = [self getVal:[_points objectAtIndex:1] relative:height]; + CGFloat w = [self getVal:[_points objectAtIndex:2] relative:width]; + CGFloat h = [self getVal:[_points objectAtIndex:3] relative:height]; - CGAffineTransform viewbox = [self.pattern.svgView getViewBoxTransform]; + CGAffineTransform viewbox = [self.pattern.svgView getViewBoxTransform]; #if TARGET_OS_OSX - // This is needed because macOS and iOS have different conventions for where the origin is. - // For macOS, it's in the bottom-left corner. For iOS, it's in the top-left corner. - viewbox = CGAffineTransformScale(viewbox, 1, -1); + // This is needed because macOS and iOS have different conventions for where the origin is. + // For macOS, it's in the bottom-left corner. For iOS, it's in the top-left corner. + viewbox = CGAffineTransformScale(viewbox, 1, -1); #endif // TARGET_OS_OSX - CGRect newBounds = CGRectMake(x, y, w, h); - CGSize size = newBounds.size; - self.useObjectBoundingBoxForContentUnits = _useContentObjectBoundingBox; - self.paintBounds = newBounds; - self.bounds = rect; + CGRect newBounds = CGRectMake(x, y, w, h); + CGSize size = newBounds.size; + self.useObjectBoundingBoxForContentUnits = _useContentObjectBoundingBox; + self.paintBounds = newBounds; + self.bounds = rect; - const CGPatternCallbacks callbacks = { 0, &PatternFunction, NULL }; - CGColorSpaceRef patternSpace = CGColorSpaceCreatePattern(NULL); - CGContextSetFillColorSpace(context, patternSpace); - CGColorSpaceRelease(patternSpace); + const CGPatternCallbacks callbacks = {0, &PatternFunction, NULL}; + CGColorSpaceRef patternSpace = CGColorSpaceCreatePattern(NULL); + CGContextSetFillColorSpace(context, patternSpace); + CGColorSpaceRelease(patternSpace); - CGPatternRef pattern = CGPatternCreate((__bridge void * _Nullable)(self), - newBounds, - viewbox, - size.width, - size.height, - kCGPatternTilingConstantSpacing, - true, - &callbacks); - CGFloat alpha = 1.0; - CGContextSetFillPattern(context, pattern, &alpha); - CGPatternRelease(pattern); + CGPatternRef pattern = CGPatternCreate( + (__bridge void *_Nullable)(self), + newBounds, + viewbox, + size.width, + size.height, + kCGPatternTilingConstantSpacing, + true, + &callbacks); + CGFloat alpha = 1.0; + CGContextSetFillPattern(context, pattern, &alpha); + CGPatternRelease(pattern); - CGContextFillRect(context, bounds); + CGContextFillRect(context, bounds); } - (void)paintLinearGradient:(CGContextRef)context bounds:(CGRect)bounds { - if ([_colors count] == 0) { - RCTLogWarn(@"No stops in gradient"); - return; - } - CGGradientRef gradient = CGGradientRetain([RCTConvert RNSVGCGGradient:_colors]); - CGGradientDrawingOptions extendOptions = kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation; + if ([_colors count] == 0) { + RCTLogWarn(@"No stops in gradient"); + return; + } + CGGradientRef gradient = CGGradientRetain([RCTConvert RNSVGCGGradient:_colors]); + CGGradientDrawingOptions extendOptions = kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation; - CGRect rect = [self getPaintRect:context bounds:bounds]; - CGFloat height = CGRectGetHeight(rect); - CGFloat width = CGRectGetWidth(rect); - CGFloat offsetX = CGRectGetMinX(rect); - CGFloat offsetY = CGRectGetMinY(rect); + CGRect rect = [self getPaintRect:context bounds:bounds]; + CGFloat height = CGRectGetHeight(rect); + CGFloat width = CGRectGetWidth(rect); + CGFloat offsetX = CGRectGetMinX(rect); + CGFloat offsetY = CGRectGetMinY(rect); - CGFloat x1 = [self getVal:[_points objectAtIndex:0] relative:width] + offsetX; - CGFloat y1 = [self getVal:[_points objectAtIndex:1] relative:height] + offsetY; - CGFloat x2 = [self getVal:[_points objectAtIndex:2] relative:width] + offsetX; - CGFloat y2 = [self getVal:[_points objectAtIndex:3] relative:height] + offsetY; + CGFloat x1 = [self getVal:[_points objectAtIndex:0] relative:width] + offsetX; + CGFloat y1 = [self getVal:[_points objectAtIndex:1] relative:height] + offsetY; + CGFloat x2 = [self getVal:[_points objectAtIndex:2] relative:width] + offsetX; + CGFloat y2 = [self getVal:[_points objectAtIndex:3] relative:height] + offsetY; - CGContextConcatCTM(context, _transform); - CGContextDrawLinearGradient(context, gradient, CGPointMake(x1, y1), CGPointMake(x2, y2), extendOptions); - CGGradientRelease(gradient); + CGContextConcatCTM(context, _transform); + CGContextDrawLinearGradient(context, gradient, CGPointMake(x1, y1), CGPointMake(x2, y2), extendOptions); + CGGradientRelease(gradient); } - (void)paintRadialGradient:(CGContextRef)context bounds:(CGRect)bounds { - if ([_colors count] == 0) { - RCTLogWarn(@"No stops in gradient"); - return; - } - CGGradientRef gradient = CGGradientRetain([RCTConvert RNSVGCGGradient:_colors]); - CGGradientDrawingOptions extendOptions = kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation; + if ([_colors count] == 0) { + RCTLogWarn(@"No stops in gradient"); + return; + } + CGGradientRef gradient = CGGradientRetain([RCTConvert RNSVGCGGradient:_colors]); + CGGradientDrawingOptions extendOptions = kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation; - CGRect rect = [self getPaintRect:context bounds:bounds]; + CGRect rect = [self getPaintRect:context bounds:bounds]; - CGFloat width = CGRectGetWidth(rect); - CGFloat height = CGRectGetHeight(rect); + CGFloat width = CGRectGetWidth(rect); + CGFloat height = CGRectGetHeight(rect); - CGFloat offsetX = CGRectGetMinX(rect); - CGFloat offsetY = CGRectGetMinY(rect); + CGFloat offsetX = CGRectGetMinX(rect); + CGFloat offsetY = CGRectGetMinY(rect); - CGFloat rx = [self getVal:[_points objectAtIndex:2] relative:width]; - CGFloat ry = [self getVal:[_points objectAtIndex:3] relative:height]; + CGFloat rx = [self getVal:[_points objectAtIndex:2] relative:width]; + CGFloat ry = [self getVal:[_points objectAtIndex:3] relative:height]; - double ratio = ry / rx; + double ratio = ry / rx; - CGFloat fx = [self getVal:[_points objectAtIndex:0] relative:width] + offsetX; - CGFloat fy = ([self getVal:[_points objectAtIndex:1] relative:height] + offsetY) / ratio; + CGFloat fx = [self getVal:[_points objectAtIndex:0] relative:width] + offsetX; + CGFloat fy = ([self getVal:[_points objectAtIndex:1] relative:height] + offsetY) / ratio; - CGFloat cx = [self getVal:[_points objectAtIndex:4] relative:width] + offsetX; - CGFloat cy = ([self getVal:[_points objectAtIndex:5] relative:height] + offsetY) / ratio; + CGFloat cx = [self getVal:[_points objectAtIndex:4] relative:width] + offsetX; + CGFloat cy = ([self getVal:[_points objectAtIndex:5] relative:height] + offsetY) / ratio; - CGAffineTransform transform = CGAffineTransformMakeScale(1, ratio); - CGContextConcatCTM(context, transform); + CGAffineTransform transform = CGAffineTransformMakeScale(1, ratio); + CGContextConcatCTM(context, transform); - CGContextConcatCTM(context, _transform); - CGContextDrawRadialGradient(context, gradient, CGPointMake(fx, fy), 0, CGPointMake(cx, cy), rx, extendOptions); - CGGradientRelease(gradient); + CGContextConcatCTM(context, _transform); + CGContextDrawRadialGradient(context, gradient, CGPointMake(fx, fy), 0, CGPointMake(cx, cy), rx, extendOptions); + CGGradientRelease(gradient); } @end - diff --git a/apple/Brushes/RNSVGPainterBrush.mm b/apple/Brushes/RNSVGPainterBrush.mm index 9a429735..04589c98 100644 --- a/apple/Brushes/RNSVGPainterBrush.mm +++ b/apple/Brushes/RNSVGPainterBrush.mm @@ -7,39 +7,38 @@ */ #import "RNSVGPainterBrush.h" -#import "RNSVGPainter.h" -#import "RCTConvert+RNSVG.h" #import +#import "RCTConvert+RNSVG.h" +#import "RNSVGPainter.h" @implementation RNSVGPainterBrush - (instancetype)initWithArray:(NSArray *)array { - if ((self = [super initWithArray:array])) { - if (array.count != 2) { - RCTLogError(@"-[%@ %@] expects 2 elements, received %@", - self.class, NSStringFromSelector(_cmd), array); - return nil; - } - - self.brushRef = [array objectAtIndex:1]; + if ((self = [super initWithArray:array])) { + if (array.count != 2) { + RCTLogError(@"-[%@ %@] expects 2 elements, received %@", self.class, NSStringFromSelector(_cmd), array); + return nil; } - return self; + + self.brushRef = [array objectAtIndex:1]; + } + return self; } - (void)paint:(CGContextRef)context opacity:(CGFloat)opacity painter:(RNSVGPainter *)painter bounds:(CGRect)bounds { - BOOL transparency = opacity < 1; - if (transparency) { - CGContextSetAlpha(context, opacity); - CGContextBeginTransparencyLayer(context, NULL); - } + BOOL transparency = opacity < 1; + if (transparency) { + CGContextSetAlpha(context, opacity); + CGContextBeginTransparencyLayer(context, NULL); + } - [painter paint:context bounds:bounds]; + [painter paint:context bounds:bounds]; - if (transparency) { - CGContextEndTransparencyLayer(context); - } + if (transparency) { + CGContextEndTransparencyLayer(context); + } } @end diff --git a/apple/Brushes/RNSVGSolidColorBrush.mm b/apple/Brushes/RNSVGSolidColorBrush.mm index f2dc6d05..42a86da7 100644 --- a/apple/Brushes/RNSVGSolidColorBrush.mm +++ b/apple/Brushes/RNSVGSolidColorBrush.mm @@ -9,65 +9,63 @@ #import "RNSVGSolidColorBrush.h" #import "RNSVGUIKit.h" -#import "RCTConvert+RNSVG.h" #import +#import "RCTConvert+RNSVG.h" -@implementation RNSVGSolidColorBrush -{ - RNSVGColor *_color; +@implementation RNSVGSolidColorBrush { + RNSVGColor *_color; } - (instancetype)initWithArray:(NSArray *)array { - if ((self = [super initWithArray:array])) { - _color = [RCTConvert RNSVGColor:array offset:1]; - } - return self; + if ((self = [super initWithArray:array])) { + _color = [RCTConvert RNSVGColor:array offset:1]; + } + return self; } - (instancetype)initWithNumber:(NSNumber *)number { - if ((self = [super init])) { - _color = [RCTConvert RNSVGColor:number]; - } - return self; + if ((self = [super init])) { + _color = [RCTConvert RNSVGColor:number]; + } + return self; } - (instancetype)initWithColor:(RNSVGColor *)color { - if ((self = [super init])) { - _color = color; - } - return self; + if ((self = [super init])) { + _color = color; + } + return self; } - - (void)dealloc { - _color = nil; + _color = nil; } - (CGColorRef)getColorWithOpacity:(CGFloat)opacity { - CGColorRef baseColor = _color.CGColor; - CGColorRef color = CGColorCreateCopyWithAlpha(baseColor, opacity * CGColorGetAlpha(baseColor)); - return color; + CGColorRef baseColor = _color.CGColor; + CGColorRef color = CGColorCreateCopyWithAlpha(baseColor, opacity * CGColorGetAlpha(baseColor)); + return color; } - (BOOL)applyFillColor:(CGContextRef)context opacity:(CGFloat)opacity { - CGColorRef color = [self getColorWithOpacity:opacity]; - CGContextSetFillColorWithColor(context, color); - CGColorRelease(color); - return YES; + CGColorRef color = [self getColorWithOpacity:opacity]; + CGContextSetFillColorWithColor(context, color); + CGColorRelease(color); + return YES; } - (BOOL)applyStrokeColor:(CGContextRef)context opacity:(CGFloat)opacity { - CGColorRef color = [self getColorWithOpacity:opacity]; - CGContextSetStrokeColorWithColor(context, color); - CGColorRelease(color); - return YES; + CGColorRef color = [self getColorWithOpacity:opacity]; + CGContextSetStrokeColorWithColor(context, color); + CGColorRelease(color); + return YES; } @end diff --git a/apple/Elements/RNSVGClipPath.mm b/apple/Elements/RNSVGClipPath.mm index 28fd7018..d2f4543d 100644 --- a/apple/Elements/RNSVGClipPath.mm +++ b/apple/Elements/RNSVGClipPath.mm @@ -10,9 +10,9 @@ #ifdef RN_FABRIC_ENABLED #import -#import "RCTFabricComponentsPlugins.h" -#import "RCTConversions.h" #import +#import "RCTConversions.h" +#import "RCTFabricComponentsPlugins.h" #import "RNSVGFabricConversions.h" #endif // RN_FABRIC_ENABLED @@ -39,33 +39,32 @@ using namespace facebook::react; - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps { - const auto &newProps = *std::static_pointer_cast(props); - setCommonNodeProps(newProps, self); + const auto &newProps = *std::static_pointer_cast(props); + setCommonNodeProps(newProps, self); } - (void)prepareForRecycle { - [super prepareForRecycle]; + [super prepareForRecycle]; } #endif // RN_FABRIC_ENABLED - (void)parseReference { - self.dirty = false; - [self.svgView defineClipPath:self clipPathName:self.name]; + self.dirty = false; + [self.svgView defineClipPath:self clipPathName:self.name]; } - - (BOOL)isSimpleClipPath { - NSArray *children = self.subviews; - if (children.count == 1) { - RNSVGView* child = children[0]; - if ([child class] != [RNSVGGroup class]) { - return true; - } + NSArray *children = self.subviews; + if (children.count == 1) { + RNSVGView *child = children[0]; + if ([child class] != [RNSVGGroup class]) { + return true; } - return false; + } + return false; } @end diff --git a/apple/Elements/RNSVGDefs.mm b/apple/Elements/RNSVGDefs.mm index 35a3d91d..9c88b035 100644 --- a/apple/Elements/RNSVGDefs.mm +++ b/apple/Elements/RNSVGDefs.mm @@ -9,9 +9,9 @@ #ifdef RN_FABRIC_ENABLED #import -#import "RCTFabricComponentsPlugins.h" -#import "RCTConversions.h" #import +#import "RCTConversions.h" +#import "RCTFabricComponentsPlugins.h" #import "RNSVGFabricConversions.h" #endif // RN_FABRIC_ENABLED @@ -39,23 +39,23 @@ using namespace facebook::react; - (void)renderTo:(CGContextRef)context { - // Defs do not render + // Defs do not render } - (void)parseReference { - self.dirty = false; - [self traverseSubviews:^(RNSVGNode *node) { - if ([node isKindOfClass:[RNSVGNode class]]) { - [node parseReference]; - } - return YES; - }]; + self.dirty = false; + [self traverseSubviews:^(RNSVGNode *node) { + if ([node isKindOfClass:[RNSVGNode class]]) { + [node parseReference]; + } + return YES; + }]; } - (RNSVGPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { - return nil; + return nil; } @end diff --git a/apple/Elements/RNSVGForeignObject.mm b/apple/Elements/RNSVGForeignObject.mm index 2b74454e..5e5b2ad8 100644 --- a/apple/Elements/RNSVGForeignObject.mm +++ b/apple/Elements/RNSVGForeignObject.mm @@ -12,9 +12,9 @@ #ifdef RN_FABRIC_ENABLED #import -#import "RCTFabricComponentsPlugins.h" -#import "RCTConversions.h" #import +#import "RCTConversions.h" +#import "RCTFabricComponentsPlugins.h" #import "RNSVGFabricConversions.h" #endif // RN_FABRIC_ENABLED @@ -41,177 +41,177 @@ using namespace facebook::react; - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps { - const auto &newProps = *std::static_pointer_cast(props); + const auto &newProps = *std::static_pointer_cast(props); - self.x = RCTNSStringFromStringNilIfEmpty(newProps.x) ? [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x)] : nil; - self.y = RCTNSStringFromStringNilIfEmpty(newProps.y) ? [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y)] : nil; - if (RCTNSStringFromStringNilIfEmpty(newProps.foreignObjectheight)) { - self.foreignObjectheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.foreignObjectheight)]; - } - if (RCTNSStringFromStringNilIfEmpty(newProps.foreignObjectwidth)) { - self.foreignObjectwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.foreignObjectwidth)]; - } - if (RCTNSStringFromStringNilIfEmpty(newProps.height)) { - self.foreignObjectheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.height)]; - } - if (RCTNSStringFromStringNilIfEmpty(newProps.width)) { - self.foreignObjectwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.width)]; - } + self.x = RCTNSStringFromStringNilIfEmpty(newProps.x) + ? [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x)] + : nil; + self.y = RCTNSStringFromStringNilIfEmpty(newProps.y) + ? [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y)] + : nil; + if (RCTNSStringFromStringNilIfEmpty(newProps.foreignObjectheight)) { + self.foreignObjectheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.foreignObjectheight)]; + } + if (RCTNSStringFromStringNilIfEmpty(newProps.foreignObjectwidth)) { + self.foreignObjectwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.foreignObjectwidth)]; + } + if (RCTNSStringFromStringNilIfEmpty(newProps.height)) { + self.foreignObjectheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.height)]; + } + if (RCTNSStringFromStringNilIfEmpty(newProps.width)) { + self.foreignObjectwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.width)]; + } - setCommonGroupProps(newProps, self); + setCommonGroupProps(newProps, self); } - (void)prepareForRecycle { - [super prepareForRecycle]; - _x = nil; - _y = nil; - _foreignObjectheight = nil; - _foreignObjectwidth = nil; + [super prepareForRecycle]; + _x = nil; + _y = nil; + _foreignObjectheight = nil; + _foreignObjectwidth = nil; } #endif // RN_FABRIC_ENABLED - (RNSVGPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { - return nil; + return nil; } - (void)parseReference { - self.dirty = false; + self.dirty = false; } - (void)renderLayerTo:(CGContextRef)context rect:(CGRect)rect { - [self clip:context]; - CGContextTranslateCTM(context, [self relativeOnWidth:self.x], [self relativeOnHeight:self.y]); - CGRect clip = CGRectMake( - 0, - 0, - [self relativeOnWidth:self.foreignObjectwidth], - [self relativeOnHeight:self.foreignObjectheight] - ); - CGContextClipToRect(context, clip); - [super renderLayerTo:context rect:rect]; + [self clip:context]; + CGContextTranslateCTM(context, [self relativeOnWidth:self.x], [self relativeOnHeight:self.y]); + CGRect clip = CGRectMake( + 0, 0, [self relativeOnWidth:self.foreignObjectwidth], [self relativeOnHeight:self.foreignObjectheight]); + CGContextClipToRect(context, clip); + [super renderLayerTo:context rect:rect]; } - (void)renderGroupTo:(CGContextRef)context rect:(CGRect)rect { - [self pushGlyphContext]; + [self pushGlyphContext]; - __block CGRect bounds = CGRectNull; - - [self traverseSubviews:^(RNSVGView *node) { - if ([node isKindOfClass:[RNSVGMask class]] || [node isKindOfClass:[RNSVGClipPath class]]) { - // no-op - } else if ([node isKindOfClass:[RNSVGNode class]]) { - RNSVGNode* svgNode = (RNSVGNode*)node; - if (svgNode.display && [@"none" isEqualToString:svgNode.display]) { - return YES; - } - if (svgNode.responsible && !self.svgView.responsible) { - self.svgView.responsible = YES; - } - - if ([node isKindOfClass:[RNSVGRenderable class]]) { - [(RNSVGRenderable*)node mergeProperties:self]; - } - - [svgNode renderTo:context rect:rect]; - - CGRect nodeRect = svgNode.clientRect; - if (!CGRectIsEmpty(nodeRect)) { - bounds = CGRectUnion(bounds, nodeRect); - } - - if ([node isKindOfClass:[RNSVGRenderable class]]) { - [(RNSVGRenderable*)node resetProperties]; - } - } else if ([node isKindOfClass:[RNSVGSvgView class]]) { - RNSVGSvgView* svgView = (RNSVGSvgView*)node; - CGFloat width = [self relativeOnWidth:svgView.bbWidth]; - CGFloat height = [self relativeOnHeight:svgView.bbHeight]; - CGRect rect = CGRectMake(0, 0, width, height); - CGContextClipToRect(context, rect); - [svgView drawToContext:context withRect:rect]; - } else { - node.hidden = false; - [node.layer renderInContext:context]; - node.hidden = true; - } + __block CGRect bounds = CGRectNull; + [self traverseSubviews:^(RNSVGView *node) { + if ([node isKindOfClass:[RNSVGMask class]] || [node isKindOfClass:[RNSVGClipPath class]]) { + // no-op + } else if ([node isKindOfClass:[RNSVGNode class]]) { + RNSVGNode *svgNode = (RNSVGNode *)node; + if (svgNode.display && [@"none" isEqualToString:svgNode.display]) { return YES; - }]; - CGPathRef path = [self getPath:context]; - [self setHitArea:path]; - if (!CGRectEqualToRect(bounds, CGRectNull)) { - self.clientRect = bounds; - self.fillBounds = CGPathGetBoundingBox(path); - self.strokeBounds = CGPathGetBoundingBox(self.strokePath); - self.pathBounds = CGRectUnion(self.fillBounds, self.strokeBounds); + } + if (svgNode.responsible && !self.svgView.responsible) { + self.svgView.responsible = YES; + } - CGAffineTransform current = CGContextGetCTM(context); - CGAffineTransform svgToClientTransform = CGAffineTransformConcat(current, self.svgView.invInitialCTM); + if ([node isKindOfClass:[RNSVGRenderable class]]) { + [(RNSVGRenderable *)node mergeProperties:self]; + } - self.ctm = svgToClientTransform; - self.screenCTM = current; + [svgNode renderTo:context rect:rect]; - CGAffineTransform transform = CGAffineTransformConcat(self.matrix, self.transforms); - CGPoint mid = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds)); - CGPoint center = CGPointApplyAffineTransform(mid, transform); + CGRect nodeRect = svgNode.clientRect; + if (!CGRectIsEmpty(nodeRect)) { + bounds = CGRectUnion(bounds, nodeRect); + } - self.bounds = bounds; - if (!isnan(center.x) && !isnan(center.y)) { - self.center = center; - } - self.frame = bounds; + if ([node isKindOfClass:[RNSVGRenderable class]]) { + [(RNSVGRenderable *)node resetProperties]; + } + } else if ([node isKindOfClass:[RNSVGSvgView class]]) { + RNSVGSvgView *svgView = (RNSVGSvgView *)node; + CGFloat width = [self relativeOnWidth:svgView.bbWidth]; + CGFloat height = [self relativeOnHeight:svgView.bbHeight]; + CGRect rect = CGRectMake(0, 0, width, height); + CGContextClipToRect(context, rect); + [svgView drawToContext:context withRect:rect]; + } else { + node.hidden = false; + [node.layer renderInContext:context]; + node.hidden = true; } - [self popGlyphContext]; + return YES; + }]; + CGPathRef path = [self getPath:context]; + [self setHitArea:path]; + if (!CGRectEqualToRect(bounds, CGRectNull)) { + self.clientRect = bounds; + self.fillBounds = CGPathGetBoundingBox(path); + self.strokeBounds = CGPathGetBoundingBox(self.strokePath); + self.pathBounds = CGRectUnion(self.fillBounds, self.strokeBounds); + + CGAffineTransform current = CGContextGetCTM(context); + CGAffineTransform svgToClientTransform = CGAffineTransformConcat(current, self.svgView.invInitialCTM); + + self.ctm = svgToClientTransform; + self.screenCTM = current; + + CGAffineTransform transform = CGAffineTransformConcat(self.matrix, self.transforms); + CGPoint mid = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds)); + CGPoint center = CGPointApplyAffineTransform(mid, transform); + + self.bounds = bounds; + if (!isnan(center.x) && !isnan(center.y)) { + self.center = center; + } + self.frame = bounds; + } + + [self popGlyphContext]; } - (void)drawRect:(CGRect)rect { - [self invalidate]; + [self invalidate]; } - (void)setX:(RNSVGLength *)x { - if ([x isEqualTo:_x]) { - return; - } + if ([x isEqualTo:_x]) { + return; + } - _x = x; - [self invalidate]; + _x = x; + [self invalidate]; } - (void)setY:(RNSVGLength *)y { - if ([y isEqualTo:_y]) { - return; - } + if ([y isEqualTo:_y]) { + return; + } - _y = y; - [self invalidate]; + _y = y; + [self invalidate]; } - (void)setForeignObjectwidth:(RNSVGLength *)foreignObjectwidth { - if ([foreignObjectwidth isEqualTo:_foreignObjectwidth]) { - return; - } + if ([foreignObjectwidth isEqualTo:_foreignObjectwidth]) { + return; + } - _foreignObjectwidth = foreignObjectwidth; - [self invalidate]; + _foreignObjectwidth = foreignObjectwidth; + [self invalidate]; } - (void)setForeignObjectheight:(RNSVGLength *)foreignObjectheight { - if ([foreignObjectheight isEqualTo:_foreignObjectheight]) { - return; - } + if ([foreignObjectheight isEqualTo:_foreignObjectheight]) { + return; + } - _foreignObjectheight = foreignObjectheight; - [self invalidate]; + _foreignObjectheight = foreignObjectheight; + [self invalidate]; } @end diff --git a/apple/Elements/RNSVGGroup.h b/apple/Elements/RNSVGGroup.h index 0d7202f4..ad4259d1 100644 --- a/apple/Elements/RNSVGGroup.h +++ b/apple/Elements/RNSVGGroup.h @@ -10,11 +10,11 @@ #import "RNSVGUIKit.h" -#import "RNSVGContainer.h" #import "RNSVGCGFCRule.h" -#import "RNSVGSvgView.h" -#import "RNSVGPath.h" +#import "RNSVGContainer.h" #import "RNSVGGlyphContext.h" +#import "RNSVGPath.h" +#import "RNSVGSvgView.h" @interface RNSVGGroup : RNSVGPath diff --git a/apple/Elements/RNSVGGroup.mm b/apple/Elements/RNSVGGroup.mm index 90a61b9b..aaaafa81 100644 --- a/apple/Elements/RNSVGGroup.mm +++ b/apple/Elements/RNSVGGroup.mm @@ -10,18 +10,16 @@ #import "RNSVGClipPath.h" #import "RNSVGMask.h" - #ifdef RN_FABRIC_ENABLED #import -#import "RCTFabricComponentsPlugins.h" -#import "RCTConversions.h" #import +#import "RCTConversions.h" +#import "RCTFabricComponentsPlugins.h" #import "RNSVGFabricConversions.h" #endif // RN_FABRIC_ENABLED -@implementation RNSVGGroup -{ - RNSVGGlyphContext *_glyphContext; +@implementation RNSVGGroup { + RNSVGGlyphContext *_glyphContext; } #ifdef RN_FABRIC_ENABLED @@ -45,251 +43,251 @@ using namespace facebook::react; - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps { - const auto &newProps = *std::static_pointer_cast(props); + const auto &newProps = *std::static_pointer_cast(props); - setCommonGroupProps(newProps, self); + setCommonGroupProps(newProps, self); } - (void)prepareForRecycle { - [super prepareForRecycle]; - _font = nil; - _glyphContext = nil; + [super prepareForRecycle]; + _font = nil; + _glyphContext = nil; } #endif // RN_FABRIC_ENABLED -- (void)setFont:(NSDictionary*)font +- (void)setFont:(NSDictionary *)font { - if (font == _font) { - return; - } + if (font == _font) { + return; + } - [self invalidate]; - _font = font; + [self invalidate]; + _font = font; } - (void)renderLayerTo:(CGContextRef)context rect:(CGRect)rect { - [self clip:context]; - [self setupGlyphContext:context]; - [self renderGroupTo:context rect:rect]; + [self clip:context]; + [self setupGlyphContext:context]; + [self renderGroupTo:context rect:rect]; } - (void)renderGroupTo:(CGContextRef)context rect:(CGRect)rect { - [self pushGlyphContext]; + [self pushGlyphContext]; - __block CGRect bounds = CGRectNull; - - [self traverseSubviews:^(RNSVGView *node) { - if ([node isKindOfClass:[RNSVGMask class]] || [node isKindOfClass:[RNSVGClipPath class]]) { - // no-op - } else if ([node isKindOfClass:[RNSVGNode class]]) { - RNSVGNode* svgNode = (RNSVGNode*)node; - if (svgNode.display && [@"none" isEqualToString:svgNode.display]) { - return YES; - } - if (svgNode.responsible && !self.svgView.responsible) { - self.svgView.responsible = YES; - } - - if ([node isKindOfClass:[RNSVGRenderable class]]) { - [(RNSVGRenderable*)node mergeProperties:self]; - } - - [svgNode renderTo:context rect:rect]; - - CGRect nodeRect = svgNode.clientRect; - if (!CGRectIsEmpty(nodeRect)) { - bounds = CGRectUnion(bounds, nodeRect); - } - - if ([node isKindOfClass:[RNSVGRenderable class]]) { - [(RNSVGRenderable*)node resetProperties]; - } - } else if ([node isKindOfClass:[RNSVGSvgView class]]) { - RNSVGSvgView* svgView = (RNSVGSvgView*)node; - CGFloat width = [self relativeOnWidth:svgView.bbWidth]; - CGFloat height = [self relativeOnHeight:svgView.bbHeight]; - CGRect rect = CGRectMake(0, 0, width, height); - CGContextClipToRect(context, rect); - [svgView drawToContext:context withRect:rect]; - } else { - [node drawRect:rect]; - } + __block CGRect bounds = CGRectNull; + [self traverseSubviews:^(RNSVGView *node) { + if ([node isKindOfClass:[RNSVGMask class]] || [node isKindOfClass:[RNSVGClipPath class]]) { + // no-op + } else if ([node isKindOfClass:[RNSVGNode class]]) { + RNSVGNode *svgNode = (RNSVGNode *)node; + if (svgNode.display && [@"none" isEqualToString:svgNode.display]) { return YES; - }]; - CGPathRef path = [self getPath:context]; - [self setHitArea:path]; - if (!CGRectEqualToRect(bounds, CGRectNull)) { - self.clientRect = bounds; - self.fillBounds = CGPathGetBoundingBox(path); - self.strokeBounds = CGPathGetBoundingBox(self.strokePath); - self.pathBounds = CGRectUnion(self.fillBounds, self.strokeBounds); + } + if (svgNode.responsible && !self.svgView.responsible) { + self.svgView.responsible = YES; + } - CGAffineTransform current = CGContextGetCTM(context); - CGAffineTransform svgToClientTransform = CGAffineTransformConcat(current, self.svgView.invInitialCTM); + if ([node isKindOfClass:[RNSVGRenderable class]]) { + [(RNSVGRenderable *)node mergeProperties:self]; + } - self.ctm = svgToClientTransform; - self.screenCTM = current; + [svgNode renderTo:context rect:rect]; - CGAffineTransform transform = CGAffineTransformConcat(self.matrix, self.transforms); - CGPoint mid = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds)); - CGPoint center = CGPointApplyAffineTransform(mid, transform); + CGRect nodeRect = svgNode.clientRect; + if (!CGRectIsEmpty(nodeRect)) { + bounds = CGRectUnion(bounds, nodeRect); + } - self.bounds = bounds; - if (!isnan(center.x) && !isnan(center.y)) { - self.center = center; - } - self.frame = bounds; + if ([node isKindOfClass:[RNSVGRenderable class]]) { + [(RNSVGRenderable *)node resetProperties]; + } + } else if ([node isKindOfClass:[RNSVGSvgView class]]) { + RNSVGSvgView *svgView = (RNSVGSvgView *)node; + CGFloat width = [self relativeOnWidth:svgView.bbWidth]; + CGFloat height = [self relativeOnHeight:svgView.bbHeight]; + CGRect rect = CGRectMake(0, 0, width, height); + CGContextClipToRect(context, rect); + [svgView drawToContext:context withRect:rect]; + } else { + [node drawRect:rect]; } - [self popGlyphContext]; + return YES; + }]; + CGPathRef path = [self getPath:context]; + [self setHitArea:path]; + if (!CGRectEqualToRect(bounds, CGRectNull)) { + self.clientRect = bounds; + self.fillBounds = CGPathGetBoundingBox(path); + self.strokeBounds = CGPathGetBoundingBox(self.strokePath); + self.pathBounds = CGRectUnion(self.fillBounds, self.strokeBounds); + + CGAffineTransform current = CGContextGetCTM(context); + CGAffineTransform svgToClientTransform = CGAffineTransformConcat(current, self.svgView.invInitialCTM); + + self.ctm = svgToClientTransform; + self.screenCTM = current; + + CGAffineTransform transform = CGAffineTransformConcat(self.matrix, self.transforms); + CGPoint mid = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds)); + CGPoint center = CGPointApplyAffineTransform(mid, transform); + + self.bounds = bounds; + if (!isnan(center.x) && !isnan(center.y)) { + self.center = center; + } + self.frame = bounds; + } + + [self popGlyphContext]; } - (void)setupGlyphContext:(CGContextRef)context { - CGRect clipBounds = CGContextGetClipBoundingBox(context); - clipBounds = CGRectApplyAffineTransform(clipBounds, self.matrix); - clipBounds = CGRectApplyAffineTransform(clipBounds, self.transforms); - CGFloat width = CGRectGetWidth(clipBounds); - CGFloat height = CGRectGetHeight(clipBounds); + CGRect clipBounds = CGContextGetClipBoundingBox(context); + clipBounds = CGRectApplyAffineTransform(clipBounds, self.matrix); + clipBounds = CGRectApplyAffineTransform(clipBounds, self.transforms); + CGFloat width = CGRectGetWidth(clipBounds); + CGFloat height = CGRectGetHeight(clipBounds); - _glyphContext = [[RNSVGGlyphContext alloc] initWithWidth:width - height:height]; + _glyphContext = [[RNSVGGlyphContext alloc] initWithWidth:width height:height]; } - (RNSVGGlyphContext *)getGlyphContext { - return _glyphContext; + return _glyphContext; } - (void)pushGlyphContext { - __typeof__(self) __weak weakSelf = self; - [[self.textRoot getGlyphContext] pushContext:weakSelf font:self.font]; + __typeof__(self) __weak weakSelf = self; + [[self.textRoot getGlyphContext] pushContext:weakSelf font:self.font]; } - (void)popGlyphContext { - [[self.textRoot getGlyphContext] popContext]; + [[self.textRoot getGlyphContext] popContext]; } - (void)renderPathTo:(CGContextRef)context rect:(CGRect)rect { - [super renderLayerTo:context rect:rect]; + [super renderLayerTo:context rect:rect]; } - (CGPathRef)getPath:(CGContextRef)context { - CGPathRef cached = self.path; - if (cached) { - return cached; - } - CGMutablePathRef __block path = CGPathCreateMutable(); - [self traverseSubviews:^(RNSVGNode *node) { - if ([node isKindOfClass:[RNSVGNode class]] && ![node isKindOfClass:[RNSVGMask class]]) { - CGAffineTransform transform = CGAffineTransformConcat(node.matrix, node.transforms); - CGPathAddPath(path, &transform, [node getPath:context]); - CGPathAddPath(path, &transform, [node markerPath]); - node.dirty = false; - } - return YES; - }]; - - cached = CGPathRetain((CGPathRef)CFAutorelease(path)); - self.path = cached; + CGPathRef cached = self.path; + if (cached) { return cached; + } + CGMutablePathRef __block path = CGPathCreateMutable(); + [self traverseSubviews:^(RNSVGNode *node) { + if ([node isKindOfClass:[RNSVGNode class]] && ![node isKindOfClass:[RNSVGMask class]]) { + CGAffineTransform transform = CGAffineTransformConcat(node.matrix, node.transforms); + CGPathAddPath(path, &transform, [node getPath:context]); + CGPathAddPath(path, &transform, [node markerPath]); + node.dirty = false; + } + return YES; + }]; + + cached = CGPathRetain((CGPathRef)CFAutorelease(path)); + self.path = cached; + return cached; } - (RNSVGPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { - CGPoint transformed = CGPointApplyAffineTransform(point, self.invmatrix); - transformed = CGPointApplyAffineTransform(transformed, self.invTransform); - - if (!CGRectContainsPoint(self.pathBounds, transformed)) { - return nil; - } - - if (self.clipPath) { - RNSVGClipPath *clipNode = (RNSVGClipPath*)[self.svgView getDefinedClipPath:self.clipPath]; - if ([clipNode isSimpleClipPath]) { - CGPathRef clipPath = [self getClipPath]; - if (clipPath && !CGPathContainsPoint(clipPath, nil, transformed, clipNode.clipRule == kRNSVGCGFCRuleEvenodd)) { - return nil; - } - } else { - RNSVGRenderable *clipGroup = (RNSVGRenderable*)clipNode; - if (![clipGroup hitTest:transformed withEvent:event]) { - return nil; - } - } - } - - if (!event) { - NSPredicate *const anyActive = [NSPredicate predicateWithFormat:@"self isKindOfClass: %@ AND active == TRUE", [RNSVGNode class]]; - NSArray *const filtered = [self.subviews filteredArrayUsingPredicate:anyActive]; - if ([filtered count] != 0) { - return [filtered.lastObject hitTest:transformed withEvent:event]; - } - } - - for (RNSVGView *node in [self.subviews reverseObjectEnumerator]) { - if ([node isKindOfClass:[RNSVGNode class]]) { - if ([node isKindOfClass:[RNSVGMask class]]) { - continue; - } - RNSVGNode* svgNode = (RNSVGNode*)node; - if (event) { - svgNode.active = NO; - } - RNSVGPlatformView *hitChild = [svgNode hitTest:transformed withEvent:event]; - if (hitChild) { - svgNode.active = YES; - return (svgNode.responsible || (svgNode != hitChild)) ? hitChild : self; - } - } else if ([node isKindOfClass:[RNSVGSvgView class]]) { - RNSVGSvgView* svgView = (RNSVGSvgView*)node; - RNSVGPlatformView *hitChild = [svgView hitTest:transformed withEvent:event]; - if (hitChild) { - return hitChild; - } - } - } - - RNSVGPlatformView *hitSelf = [super hitTest:transformed withEvent:event]; - if (hitSelf) { - return hitSelf; - } + CGPoint transformed = CGPointApplyAffineTransform(point, self.invmatrix); + transformed = CGPointApplyAffineTransform(transformed, self.invTransform); + if (!CGRectContainsPoint(self.pathBounds, transformed)) { return nil; + } + + if (self.clipPath) { + RNSVGClipPath *clipNode = (RNSVGClipPath *)[self.svgView getDefinedClipPath:self.clipPath]; + if ([clipNode isSimpleClipPath]) { + CGPathRef clipPath = [self getClipPath]; + if (clipPath && !CGPathContainsPoint(clipPath, nil, transformed, clipNode.clipRule == kRNSVGCGFCRuleEvenodd)) { + return nil; + } + } else { + RNSVGRenderable *clipGroup = (RNSVGRenderable *)clipNode; + if (![clipGroup hitTest:transformed withEvent:event]) { + return nil; + } + } + } + + if (!event) { + NSPredicate *const anyActive = + [NSPredicate predicateWithFormat:@"self isKindOfClass: %@ AND active == TRUE", [RNSVGNode class]]; + NSArray *const filtered = [self.subviews filteredArrayUsingPredicate:anyActive]; + if ([filtered count] != 0) { + return [filtered.lastObject hitTest:transformed withEvent:event]; + } + } + + for (RNSVGView *node in [self.subviews reverseObjectEnumerator]) { + if ([node isKindOfClass:[RNSVGNode class]]) { + if ([node isKindOfClass:[RNSVGMask class]]) { + continue; + } + RNSVGNode *svgNode = (RNSVGNode *)node; + if (event) { + svgNode.active = NO; + } + RNSVGPlatformView *hitChild = [svgNode hitTest:transformed withEvent:event]; + if (hitChild) { + svgNode.active = YES; + return (svgNode.responsible || (svgNode != hitChild)) ? hitChild : self; + } + } else if ([node isKindOfClass:[RNSVGSvgView class]]) { + RNSVGSvgView *svgView = (RNSVGSvgView *)node; + RNSVGPlatformView *hitChild = [svgView hitTest:transformed withEvent:event]; + if (hitChild) { + return hitChild; + } + } + } + + RNSVGPlatformView *hitSelf = [super hitTest:transformed withEvent:event]; + if (hitSelf) { + return hitSelf; + } + + return nil; } - (void)parseReference { - self.dirty = false; - if (self.name) { - __typeof__(self) __weak weakSelf = self; - [self.svgView defineTemplate:weakSelf templateName:self.name]; - } + self.dirty = false; + if (self.name) { + __typeof__(self) __weak weakSelf = self; + [self.svgView defineTemplate:weakSelf templateName:self.name]; + } - [self traverseSubviews:^(RNSVGNode *node) { - if ([node isKindOfClass:[RNSVGNode class]]) { - [node parseReference]; - } - return YES; - }]; + [self traverseSubviews:^(RNSVGNode *node) { + if ([node isKindOfClass:[RNSVGNode class]]) { + [node parseReference]; + } + return YES; + }]; } - (void)resetProperties { - [self traverseSubviews:^(__kindof RNSVGNode *node) { - if ([node isKindOfClass:[RNSVGRenderable class]]) { - [(RNSVGRenderable*)node resetProperties]; - } - return YES; - }]; + [self traverseSubviews:^(__kindof RNSVGNode *node) { + if ([node isKindOfClass:[RNSVGRenderable class]]) { + [(RNSVGRenderable *)node resetProperties]; + } + return YES; + }]; } @end diff --git a/apple/Elements/RNSVGImage.h b/apple/Elements/RNSVGImage.h index 0383d5d7..18854041 100644 --- a/apple/Elements/RNSVGImage.h +++ b/apple/Elements/RNSVGImage.h @@ -9,9 +9,9 @@ #import #import +#import "RNSVGLength.h" #import "RNSVGRenderable.h" #import "RNSVGVBMOS.h" -#import "RNSVGLength.h" #import @@ -19,10 +19,10 @@ @property (nonatomic, weak) RCTBridge *bridge; @property (nonatomic, assign) id src; -@property (nonatomic, strong) RNSVGLength* x; -@property (nonatomic, strong) RNSVGLength* y; -@property (nonatomic, strong) RNSVGLength* imagewidth; -@property (nonatomic, strong) RNSVGLength* imageheight; +@property (nonatomic, strong) RNSVGLength *x; +@property (nonatomic, strong) RNSVGLength *y; +@property (nonatomic, strong) RNSVGLength *imagewidth; +@property (nonatomic, strong) RNSVGLength *imageheight; @property (nonatomic, strong) NSString *align; @property (nonatomic, assign) RNSVGVBMOS meetOrSlice; diff --git a/apple/Elements/RNSVGImage.mm b/apple/Elements/RNSVGImage.mm index 7445b839..af151543 100644 --- a/apple/Elements/RNSVGImage.mm +++ b/apple/Elements/RNSVGImage.mm @@ -15,25 +15,25 @@ #else -#import -#import -#import #import +#import +#import +#import #endif // RN_FABRIC_ENABLED #import -#import "RNSVGViewBox.h" #import "RCTBridge.h" +#import "RNSVGViewBox.h" #ifdef RN_FABRIC_ENABLED #import -#import "RCTFabricComponentsPlugins.h" -#import "RCTConversions.h" #import -#import "RNSVGFabricConversions.h" +#import "RCTConversions.h" +#import "RCTFabricComponentsPlugins.h" #import "RCTImagePrimitivesConversions.h" #import "RCTImageSource.h" +#import "RNSVGFabricConversions.h" // Some RN private method hacking below similar to how it is done in RNScreens: // https://github.com/software-mansion/react-native-screens/blob/90e548739f35b5ded2524a9d6410033fc233f586/ios/RNSScreenStackHeaderConfig.mm#L30 @@ -43,11 +43,10 @@ #endif // RN_FABRIC_ENABLED -@implementation RNSVGImage -{ - CGImageRef _image; - CGSize _imageSize; - RCTImageLoaderCancellationBlock _reloadImageCancellationBlock; +@implementation RNSVGImage { + CGImageRef _image; + CGSize _imageSize; + RCTImageLoaderCancellationBlock _reloadImageCancellationBlock; } #ifdef RN_FABRIC_ENABLED using namespace facebook::react; @@ -71,104 +70,106 @@ using namespace facebook::react; - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps { const auto &newProps = *std::static_pointer_cast(props); - const auto &oldImageProps = *std::static_pointer_cast(oldProps); + const auto &oldImageProps = *std::static_pointer_cast(oldProps); - self.x = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x)]; - self.y = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y)]; - if (RCTNSStringFromStringNilIfEmpty(newProps.imageheight)) { - self.imageheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.imageheight)]; - } - if (RCTNSStringFromStringNilIfEmpty(newProps.imagewidth)) { - self.imagewidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.imagewidth)]; - } - if (RCTNSStringFromStringNilIfEmpty(newProps.height)) { - self.imageheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.height)]; - } - if (RCTNSStringFromStringNilIfEmpty(newProps.width)) { - self.imagewidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.width)]; - } - - if (oldProps == nullptr || oldImageProps.src != newProps.src) { - // TODO: make it the same as in e.g. slider - NSURLRequest *request = NSURLRequestFromImageSource(newProps.src); - CGSize size = RCTCGSizeFromSize(newProps.src.size); - CGFloat scale = newProps.src.scale; - RCTImageSource *imageSource = [[RCTImageSource alloc] initWithURLRequest:request size:size scale:scale]; - [self setImageSrc:imageSource request:request]; - } - self.align = RCTNSStringFromStringNilIfEmpty(newProps.align); - self.meetOrSlice = intToRNSVGVBMOS(newProps.meetOrSlice); + self.x = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x)]; + self.y = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y)]; + if (RCTNSStringFromStringNilIfEmpty(newProps.imageheight)) { + self.imageheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.imageheight)]; + } + if (RCTNSStringFromStringNilIfEmpty(newProps.imagewidth)) { + self.imagewidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.imagewidth)]; + } + if (RCTNSStringFromStringNilIfEmpty(newProps.height)) { + self.imageheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.height)]; + } + if (RCTNSStringFromStringNilIfEmpty(newProps.width)) { + self.imagewidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.width)]; + } - setCommonRenderableProps(newProps, self); + if (oldProps == nullptr || oldImageProps.src != newProps.src) { + // TODO: make it the same as in e.g. slider + NSURLRequest *request = NSURLRequestFromImageSource(newProps.src); + CGSize size = RCTCGSizeFromSize(newProps.src.size); + CGFloat scale = newProps.src.scale; + RCTImageSource *imageSource = [[RCTImageSource alloc] initWithURLRequest:request size:size scale:scale]; + [self setImageSrc:imageSource request:request]; + } + self.align = RCTNSStringFromStringNilIfEmpty(newProps.align); + self.meetOrSlice = intToRNSVGVBMOS(newProps.meetOrSlice); + + setCommonRenderableProps(newProps, self); } - (void)prepareForRecycle { - [super prepareForRecycle]; - _x = nil; - _y = nil; - _imageheight = nil; - _imagewidth = nil; - _src = nil; - _align = nil; - _meetOrSlice = kRNSVGVBMOSMeet; - - if (_image) { - CGImageRelease(_image); - } - _image = nil; - _imageSize = CGSizeZero; - _reloadImageCancellationBlock = nil; + [super prepareForRecycle]; + _x = nil; + _y = nil; + _imageheight = nil; + _imagewidth = nil; + _src = nil; + _align = nil; + _meetOrSlice = kRNSVGVBMOSMeet; + + if (_image) { + CGImageRelease(_image); + } + _image = nil; + _imageSize = CGSizeZero; + _reloadImageCancellationBlock = nil; } #endif // RN_FABRIC_ENABLED - (void)setSrc:(id)src { - if (src == _src) { - return; - } - _src = src; - CGImageRelease(_image); - _image = nil; - RCTImageSource *source = [RCTConvert RCTImageSource:src]; - if (source.size.width != 0 && source.size.height != 0) { - _imageSize = source.size; - } else { - _imageSize = CGSizeMake(0, 0); - } + if (src == _src) { + return; + } + _src = src; + CGImageRelease(_image); + _image = nil; + RCTImageSource *source = [RCTConvert RCTImageSource:src]; + if (source.size.width != 0 && source.size.height != 0) { + _imageSize = source.size; + } else { + _imageSize = CGSizeMake(0, 0); + } - RCTImageLoaderCancellationBlock previousCancellationBlock = _reloadImageCancellationBlock; - if (previousCancellationBlock) { - previousCancellationBlock(); - _reloadImageCancellationBlock = nil; - } + RCTImageLoaderCancellationBlock previousCancellationBlock = _reloadImageCancellationBlock; + if (previousCancellationBlock) { + previousCancellationBlock(); + _reloadImageCancellationBlock = nil; + } - _reloadImageCancellationBlock = [[self.bridge moduleForName:@"ImageLoader"] loadImageWithURLRequest:[RCTConvert NSURLRequest:src] callback:^(NSError *error, UIImage *image) { - dispatch_async(dispatch_get_main_queue(), ^{ - self->_image = CGImageRetain(image.CGImage); - self->_imageSize = CGSizeMake(CGImageGetWidth(self->_image), CGImageGetHeight(self->_image)); - [self invalidate]; - }); - }]; + _reloadImageCancellationBlock = [[self.bridge moduleForName:@"ImageLoader"] + loadImageWithURLRequest:[RCTConvert NSURLRequest:src] + callback:^(NSError *error, UIImage *image) { + dispatch_async(dispatch_get_main_queue(), ^{ + self->_image = CGImageRetain(image.CGImage); + self->_imageSize = CGSizeMake(CGImageGetWidth(self->_image), CGImageGetHeight(self->_image)); + [self invalidate]; + }); + }]; } - (void)setImageSrc:(RCTImageSource *)source request:(NSURLRequest *)request { - CGImageRelease(_image); - _image = nil; - if (source.size.width != 0 && source.size.height != 0) { - _imageSize = source.size; - } else { - _imageSize = CGSizeMake(0, 0); - } + CGImageRelease(_image); + _image = nil; + if (source.size.width != 0 && source.size.height != 0) { + _imageSize = source.size; + } else { + _imageSize = CGSizeMake(0, 0); + } - RCTImageLoaderCancellationBlock previousCancellationBlock = _reloadImageCancellationBlock; - if (previousCancellationBlock) { - previousCancellationBlock(); - _reloadImageCancellationBlock = nil; - } + RCTImageLoaderCancellationBlock previousCancellationBlock = _reloadImageCancellationBlock; + if (previousCancellationBlock) { + previousCancellationBlock(); + _reloadImageCancellationBlock = nil; + } - _reloadImageCancellationBlock = [[ + _reloadImageCancellationBlock = [[ #ifdef RN_FABRIC_ENABLED [RCTBridge currentBridge] #else @@ -185,130 +186,133 @@ using namespace facebook::react; - (void)setX:(RNSVGLength *)x { - if ([x isEqualTo:_x]) { - return; - } - [self invalidate]; - _x = x; + if ([x isEqualTo:_x]) { + return; + } + [self invalidate]; + _x = x; } - (void)setY:(RNSVGLength *)y { - if ([y isEqualTo:_y]) { - return; - } - [self invalidate]; - _y = y; + if ([y isEqualTo:_y]) { + return; + } + [self invalidate]; + _y = y; } - (void)setImagewidth:(RNSVGLength *)width { - if ([width isEqualTo:_imagewidth]) { - return; - } - [self invalidate]; - _imagewidth = width; + if ([width isEqualTo:_imagewidth]) { + return; + } + [self invalidate]; + _imagewidth = width; } - (void)setImageheight:(RNSVGLength *)height { - if ([height isEqualTo:_imageheight]) { - return; - } - [self invalidate]; - _imageheight = height; + if ([height isEqualTo:_imageheight]) { + return; + } + [self invalidate]; + _imageheight = height; } - (void)setAlign:(NSString *)align { - if ([align isEqualToString:_align]) { - return; - } - [self invalidate]; - _align = align; + if ([align isEqualToString:_align]) { + return; + } + [self invalidate]; + _align = align; } - (void)setMeetOrSlice:(RNSVGVBMOS)meetOrSlice { - if (meetOrSlice == _meetOrSlice) { - return; - } - [self invalidate]; - _meetOrSlice = meetOrSlice; + if (meetOrSlice == _meetOrSlice) { + return; + } + [self invalidate]; + _meetOrSlice = meetOrSlice; } - (void)dealloc { - CGImageRelease(_image); + CGImageRelease(_image); } - (void)renderLayerTo:(CGContextRef)context rect:(CGRect)rect { - if (CGSizeEqualToSize(CGSizeZero, _imageSize)) { - return; - } - CGContextSaveGState(context); + if (CGSizeEqualToSize(CGSizeZero, _imageSize)) { + return; + } + CGContextSaveGState(context); - // add hit area - CGRect hitArea = [self getHitArea]; - CGPathRef hitAreaPath = CGPathCreateWithRect(hitArea, nil); - [self setHitArea:hitAreaPath]; - CGPathRelease(hitAreaPath); - self.pathBounds = hitArea; - self.fillBounds = hitArea; - self.strokeBounds = hitArea; + // add hit area + CGRect hitArea = [self getHitArea]; + CGPathRef hitAreaPath = CGPathCreateWithRect(hitArea, nil); + [self setHitArea:hitAreaPath]; + CGPathRelease(hitAreaPath); + self.pathBounds = hitArea; + self.fillBounds = hitArea; + self.strokeBounds = hitArea; - // apply viewBox transform on Image render. - CGRect imageBounds = CGRectMake(0, 0, _imageSize.width, _imageSize.height); - CGAffineTransform viewbox = [RNSVGViewBox getTransform:imageBounds eRect:hitArea align:self.align meetOrSlice:self.meetOrSlice]; + // apply viewBox transform on Image render. + CGRect imageBounds = CGRectMake(0, 0, _imageSize.width, _imageSize.height); + CGAffineTransform viewbox = [RNSVGViewBox getTransform:imageBounds + eRect:hitArea + align:self.align + meetOrSlice:self.meetOrSlice]; - [self clip:context]; - CGContextClipToRect(context, hitArea); - CGContextConcatCTM(context, viewbox); - CGContextTranslateCTM(context, 0, imageBounds.size.height); - CGContextScaleCTM(context, 1, -1); - CGContextDrawImage(context, imageBounds, _image); - CGContextRestoreGState(context); + [self clip:context]; + CGContextClipToRect(context, hitArea); + CGContextConcatCTM(context, viewbox); + CGContextTranslateCTM(context, 0, imageBounds.size.height); + CGContextScaleCTM(context, 1, -1); + CGContextDrawImage(context, imageBounds, _image); + CGContextRestoreGState(context); - CGRect bounds = hitArea; - self.clientRect = bounds; + CGRect bounds = hitArea; + self.clientRect = bounds; - CGAffineTransform current = CGContextGetCTM(context); - CGAffineTransform svgToClientTransform = CGAffineTransformConcat(current, self.svgView.invInitialCTM); + CGAffineTransform current = CGContextGetCTM(context); + CGAffineTransform svgToClientTransform = CGAffineTransformConcat(current, self.svgView.invInitialCTM); - self.ctm = svgToClientTransform; - self.screenCTM = current; + self.ctm = svgToClientTransform; + self.screenCTM = current; - CGAffineTransform transform = CGAffineTransformConcat(self.matrix, self.transforms); - CGPoint mid = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds)); - CGPoint center = CGPointApplyAffineTransform(mid, transform); + CGAffineTransform transform = CGAffineTransformConcat(self.matrix, self.transforms); + CGPoint mid = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds)); + CGPoint center = CGPointApplyAffineTransform(mid, transform); - self.bounds = bounds; - if (!isnan(center.x) && !isnan(center.y)) { - self.center = center; - } - self.frame = bounds; + self.bounds = bounds; + if (!isnan(center.x) && !isnan(center.y)) { + self.center = center; + } + self.frame = bounds; } - (CGRect)getHitArea { - CGFloat x = [self relativeOnWidth:self.x]; - CGFloat y = [self relativeOnHeight:self.y]; - CGFloat width = [self relativeOnWidth:self.imagewidth]; - CGFloat height = [self relativeOnHeight:self.imageheight]; - if (width == 0) { - width = _imageSize.width; - } - if (height == 0) { - height = _imageSize.height; - } + CGFloat x = [self relativeOnWidth:self.x]; + CGFloat y = [self relativeOnHeight:self.y]; + CGFloat width = [self relativeOnWidth:self.imagewidth]; + CGFloat height = [self relativeOnHeight:self.imageheight]; + if (width == 0) { + width = _imageSize.width; + } + if (height == 0) { + height = _imageSize.height; + } - return CGRectMake(x, y, width, height); + return CGRectMake(x, y, width, height); } - (CGPathRef)getPath:(CGContextRef)context { - return (CGPathRef)CFAutorelease(CGPathCreateWithRect([self getHitArea], nil)); + return (CGPathRef)CFAutorelease(CGPathCreateWithRect([self getHitArea], nil)); } @end diff --git a/apple/Elements/RNSVGLinearGradient.h b/apple/Elements/RNSVGLinearGradient.h index 30e577a7..870b1c7c 100644 --- a/apple/Elements/RNSVGLinearGradient.h +++ b/apple/Elements/RNSVGLinearGradient.h @@ -6,8 +6,8 @@ * LICENSE file in the root directory of this source tree. */ -#import "RNSVGNode.h" #import "RNSVGLength.h" +#import "RNSVGNode.h" @interface RNSVGLinearGradient : RNSVGNode diff --git a/apple/Elements/RNSVGLinearGradient.mm b/apple/Elements/RNSVGLinearGradient.mm index 52834d8e..88b166ce 100644 --- a/apple/Elements/RNSVGLinearGradient.mm +++ b/apple/Elements/RNSVGLinearGradient.mm @@ -6,14 +6,14 @@ * LICENSE file in the root directory of this source tree. */ #import "RNSVGLinearGradient.h" -#import "RNSVGPainter.h" #import "RNSVGBrushType.h" +#import "RNSVGPainter.h" #ifdef RN_FABRIC_ENABLED #import -#import "RCTFabricComponentsPlugins.h" -#import "RCTConversions.h" #import +#import "RCTConversions.h" +#import "RCTFabricComponentsPlugins.h" #import "RNSVGFabricConversions.h" #endif // RN_FABRIC_ENABLED @@ -40,133 +40,139 @@ using namespace facebook::react; - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps { - const auto &newProps = *std::static_pointer_cast(props); + const auto &newProps = *std::static_pointer_cast(props); - self.x1 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x1)]; - self.y1 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y1)]; - self.x2 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x2)]; - self.y2 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y2)]; - if (newProps.gradient.size() > 0) { - NSMutableArray *gradientArray = [NSMutableArray new]; - for (auto number : newProps.gradient) { - [gradientArray addObject:[NSNumber numberWithDouble:number]]; - } - self.gradient = gradientArray; + self.x1 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x1)]; + self.y1 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y1)]; + self.x2 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x2)]; + self.y2 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y2)]; + if (newProps.gradient.size() > 0) { + NSMutableArray *gradientArray = [NSMutableArray new]; + for (auto number : newProps.gradient) { + [gradientArray addObject:[NSNumber numberWithDouble:number]]; } - self.gradientUnits = newProps.gradientUnits == 0 ? kRNSVGUnitsObjectBoundingBox : kRNSVGUnitsUserSpaceOnUse; - if (newProps.gradientTransform.size() == 6) { - self.gradientTransform = CGAffineTransformMake(newProps.gradientTransform.at(0), newProps.gradientTransform.at(1), newProps.gradientTransform.at(2), newProps.gradientTransform.at(3), newProps.gradientTransform.at(4), newProps.gradientTransform.at(5)); - } - - setCommonNodeProps(newProps, self); + self.gradient = gradientArray; + } + self.gradientUnits = newProps.gradientUnits == 0 ? kRNSVGUnitsObjectBoundingBox : kRNSVGUnitsUserSpaceOnUse; + if (newProps.gradientTransform.size() == 6) { + self.gradientTransform = CGAffineTransformMake( + newProps.gradientTransform.at(0), + newProps.gradientTransform.at(1), + newProps.gradientTransform.at(2), + newProps.gradientTransform.at(3), + newProps.gradientTransform.at(4), + newProps.gradientTransform.at(5)); + } + + setCommonNodeProps(newProps, self); } - (void)prepareForRecycle { - [super prepareForRecycle]; - _x1 = nil; - _y1 = nil; - _x2 = nil; - _y2 = nil; - _gradient = nil; - _gradientUnits = kRNSVGUnitsObjectBoundingBox; - _gradientTransform = CGAffineTransformIdentity; + [super prepareForRecycle]; + _x1 = nil; + _y1 = nil; + _x2 = nil; + _y2 = nil; + _gradient = nil; + _gradientUnits = kRNSVGUnitsObjectBoundingBox; + _gradientTransform = CGAffineTransformIdentity; } #endif // RN_FABRIC_ENABLED - (instancetype)init { - if (self = [super init]) { - _gradientTransform = CGAffineTransformIdentity; - } - return self; + if (self = [super init]) { + _gradientTransform = CGAffineTransformIdentity; + } + return self; } - (void)setX1:(RNSVGLength *)x1 { - if ([x1 isEqualTo:_x1]) { - return; - } + if ([x1 isEqualTo:_x1]) { + return; + } - _x1 = x1; - [self invalidate]; + _x1 = x1; + [self invalidate]; } - (void)setY1:(RNSVGLength *)y1 { - if ([y1 isEqualTo:_y1]) { - return; - } + if ([y1 isEqualTo:_y1]) { + return; + } - _y1 = y1; - [self invalidate]; + _y1 = y1; + [self invalidate]; } - (void)setX2:(RNSVGLength *)x2 { - if ([x2 isEqualTo:_x2]) { - return; - } + if ([x2 isEqualTo:_x2]) { + return; + } - _x2 = x2; - [self invalidate]; + _x2 = x2; + [self invalidate]; } - (void)setY2:(RNSVGLength *)y2 { - if ([y2 isEqualTo:_y2]) { - return; - } + if ([y2 isEqualTo:_y2]) { + return; + } - _y2 = y2; - [self invalidate]; + _y2 = y2; + [self invalidate]; } - (void)setGradient:(NSArray *)gradient { - if (gradient == _gradient) { - return; - } + if (gradient == _gradient) { + return; + } - _gradient = gradient; - [self invalidate]; + _gradient = gradient; + [self invalidate]; } - (void)setGradientUnits:(RNSVGUnits)gradientUnits { - if (gradientUnits == _gradientUnits) { - return; - } + if (gradientUnits == _gradientUnits) { + return; + } - _gradientUnits = gradientUnits; - [self invalidate]; + _gradientUnits = gradientUnits; + [self invalidate]; } - (void)setGradientTransform:(CGAffineTransform)gradientTransform { - _gradientTransform = gradientTransform; - [self invalidate]; + _gradientTransform = gradientTransform; + [self invalidate]; } - (RNSVGPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { - return nil; + return nil; } - (void)parseReference { - self.dirty = false; - NSArray *points = @[self.x1, self.y1, self.x2, self.y2]; - RNSVGPainter *painter = [[RNSVGPainter alloc] initWithPointsArray:points]; - [painter setUnits:self.gradientUnits]; - [painter setTransform:self.gradientTransform]; - [painter setLinearGradientColors:self.gradient]; + self.dirty = false; + NSArray *points = @[ self.x1, self.y1, self.x2, self.y2 ]; + RNSVGPainter *painter = [[RNSVGPainter alloc] initWithPointsArray:points]; + [painter setUnits:self.gradientUnits]; + [painter setTransform:self.gradientTransform]; + [painter setLinearGradientColors:self.gradient]; - if (self.gradientUnits == kRNSVGUnitsUserSpaceOnUse) { - [painter setUserSpaceBoundingBox:[self.svgView getContextBounds]]; - } + if (self.gradientUnits == kRNSVGUnitsUserSpaceOnUse) { + [painter setUserSpaceBoundingBox:[self.svgView getContextBounds]]; + } - [self.svgView definePainter:painter painterName:self.name]; + [self.svgView definePainter:painter painterName:self.name]; } @end diff --git a/apple/Elements/RNSVGMarker.h b/apple/Elements/RNSVGMarker.h index a0744b9b..f98e92a2 100644 --- a/apple/Elements/RNSVGMarker.h +++ b/apple/Elements/RNSVGMarker.h @@ -19,6 +19,9 @@ @property (nonatomic, strong) NSString *align; @property (nonatomic, assign) RNSVGVBMOS meetOrSlice; -- (void)renderMarker:(CGContextRef)context rect:(CGRect)rect position:(RNSVGMarkerPosition*)position strokeWidth:(CGFloat)strokeWidth; +- (void)renderMarker:(CGContextRef)context + rect:(CGRect)rect + position:(RNSVGMarkerPosition *)position + strokeWidth:(CGFloat)strokeWidth; @end diff --git a/apple/Elements/RNSVGMarker.mm b/apple/Elements/RNSVGMarker.mm index 1ae04ed8..d76d5660 100644 --- a/apple/Elements/RNSVGMarker.mm +++ b/apple/Elements/RNSVGMarker.mm @@ -6,16 +6,16 @@ * LICENSE file in the root directory of this source tree. */ #import "RNSVGMarker.h" -#import "RNSVGPainter.h" #import "RNSVGBrushType.h" #import "RNSVGNode.h" +#import "RNSVGPainter.h" #import "RNSVGViewBox.h" #ifdef RN_FABRIC_ENABLED #import -#import "RCTFabricComponentsPlugins.h" -#import "RCTConversions.h" #import +#import "RCTConversions.h" +#import "RCTFabricComponentsPlugins.h" #import "RNSVGFabricConversions.h" #endif // RN_FABRIC_ENABLED @@ -42,230 +42,234 @@ using namespace facebook::react; - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps { - const auto &newProps = *std::static_pointer_cast(props); - - self.refX = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.refX)]; - self.refY = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.refY)]; - self.markerHeight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.markerHeight)]; - self.markerWidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.markerWidth)]; - self.markerUnits = RCTNSStringFromStringNilIfEmpty(newProps.markerUnits); - self.orient = RCTNSStringFromStringNilIfEmpty(newProps.orient); + const auto &newProps = *std::static_pointer_cast(props); - self.minX = newProps.minX; - self.minY = newProps.minY; - self.vbWidth = newProps.vbWidth; - self.vbHeight = newProps.vbHeight; - self.align = RCTNSStringFromStringNilIfEmpty(newProps.align); - self.meetOrSlice = intToRNSVGVBMOS(newProps.meetOrSlice); + self.refX = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.refX)]; + self.refY = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.refY)]; + self.markerHeight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.markerHeight)]; + self.markerWidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.markerWidth)]; + self.markerUnits = RCTNSStringFromStringNilIfEmpty(newProps.markerUnits); + self.orient = RCTNSStringFromStringNilIfEmpty(newProps.orient); - setCommonGroupProps(newProps, self); + self.minX = newProps.minX; + self.minY = newProps.minY; + self.vbWidth = newProps.vbWidth; + self.vbHeight = newProps.vbHeight; + self.align = RCTNSStringFromStringNilIfEmpty(newProps.align); + self.meetOrSlice = intToRNSVGVBMOS(newProps.meetOrSlice); + + setCommonGroupProps(newProps, self); } - (void)prepareForRecycle { - [super prepareForRecycle]; - _refX = nil; - _refY = nil; - _markerHeight = nil; - _markerWidth = nil; - _markerUnits = nil; - _orient = nil; - - _minX = 0; - _minY = 0; - _vbWidth = 0; - _vbHeight = 0; - _align = nil; - _meetOrSlice = kRNSVGVBMOSMeet; + [super prepareForRecycle]; + _refX = nil; + _refY = nil; + _markerHeight = nil; + _markerWidth = nil; + _markerUnits = nil; + _orient = nil; + + _minX = 0; + _minY = 0; + _vbWidth = 0; + _vbHeight = 0; + _align = nil; + _meetOrSlice = kRNSVGVBMOSMeet; } #endif // RN_FABRIC_ENABLED - (RNSVGPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { - return nil; + return nil; } - (void)parseReference { - self.dirty = false; - [self.svgView defineMarker:self markerName:self.name]; - [self traverseSubviews:^(RNSVGNode *node) { - if ([node isKindOfClass:[RNSVGNode class]]) { - [node parseReference]; - } - return YES; - }]; + self.dirty = false; + [self.svgView defineMarker:self markerName:self.name]; + [self traverseSubviews:^(RNSVGNode *node) { + if ([node isKindOfClass:[RNSVGNode class]]) { + [node parseReference]; + } + return YES; + }]; } - (void)setX:(RNSVGLength *)refX { - if ([refX isEqualTo:_refX]) { - return; - } + if ([refX isEqualTo:_refX]) { + return; + } - _refX = refX; - [self invalidate]; + _refX = refX; + [self invalidate]; } - (void)setY:(RNSVGLength *)refY { - if ([refY isEqualTo:_refY]) { - return; - } + if ([refY isEqualTo:_refY]) { + return; + } - _refY = refY; - [self invalidate]; + _refY = refY; + [self invalidate]; } - (void)setMarkerWidth:(RNSVGLength *)markerWidth { - if ([markerWidth isEqualTo:_markerWidth]) { - return; - } + if ([markerWidth isEqualTo:_markerWidth]) { + return; + } - _markerWidth = markerWidth; - [self invalidate]; + _markerWidth = markerWidth; + [self invalidate]; } - (void)setMarkerHeight:(RNSVGLength *)markerHeight { - if ([markerHeight isEqualTo:_markerHeight]) { - return; - } + if ([markerHeight isEqualTo:_markerHeight]) { + return; + } - _markerHeight = markerHeight; - [self invalidate]; + _markerHeight = markerHeight; + [self invalidate]; } - (void)setMarkerUnits:(NSString *)markerUnits { - if ([_markerUnits isEqualToString:markerUnits]) { - return; - } + if ([_markerUnits isEqualToString:markerUnits]) { + return; + } - _markerUnits = markerUnits; - [self invalidate]; + _markerUnits = markerUnits; + [self invalidate]; } - (void)setOrient:(NSString *)orient { - if ([orient isEqualToString:_orient]) { - return; - } + if ([orient isEqualToString:_orient]) { + return; + } - [self invalidate]; - _orient = orient; + [self invalidate]; + _orient = orient; } - (void)setMinX:(CGFloat)minX { - if (minX == _minX) { - return; - } + if (minX == _minX) { + return; + } - [self invalidate]; - _minX = minX; + [self invalidate]; + _minX = minX; } - (void)setMinY:(CGFloat)minY { - if (minY == _minY) { - return; - } + if (minY == _minY) { + return; + } - [self invalidate]; - _minY = minY; + [self invalidate]; + _minY = minY; } - (void)setVbWidth:(CGFloat)vbWidth { - if (vbWidth == _vbWidth) { - return; - } + if (vbWidth == _vbWidth) { + return; + } - [self invalidate]; - _vbWidth = vbWidth; + [self invalidate]; + _vbWidth = vbWidth; } - (void)setVbHeight:(CGFloat)vbHeight { - if (_vbHeight == vbHeight) { - return; - } + if (_vbHeight == vbHeight) { + return; + } - [self invalidate]; - _vbHeight = vbHeight; + [self invalidate]; + _vbHeight = vbHeight; } - (void)setAlign:(NSString *)align { - if ([align isEqualToString:_align]) { - return; - } + if ([align isEqualToString:_align]) { + return; + } - [self invalidate]; - _align = align; + [self invalidate]; + _align = align; } - (void)setMeetOrSlice:(RNSVGVBMOS)meetOrSlice { - if (meetOrSlice == _meetOrSlice) { - return; - } + if (meetOrSlice == _meetOrSlice) { + return; + } - [self invalidate]; - _meetOrSlice = meetOrSlice; + [self invalidate]; + _meetOrSlice = meetOrSlice; } static CGFloat RNSVG_degToRad = (CGFloat)M_PI / 180; -double deg2rad(CGFloat deg) { - return deg * RNSVG_degToRad; +double deg2rad(CGFloat deg) +{ + return deg * RNSVG_degToRad; } -- (void)renderMarker:(CGContextRef)context rect:(CGRect)rect position:(RNSVGMarkerPosition*)position strokeWidth:(CGFloat)strokeWidth +- (void)renderMarker:(CGContextRef)context + rect:(CGRect)rect + position:(RNSVGMarkerPosition *)position + strokeWidth:(CGFloat)strokeWidth { - CGContextSaveGState(context); + CGContextSaveGState(context); - CGPoint origin = [position origin]; - CGAffineTransform transform = CGAffineTransformMakeTranslation(origin.x, origin.y); + CGPoint origin = [position origin]; + CGAffineTransform transform = CGAffineTransformMakeTranslation(origin.x, origin.y); - float markerAngle = [@"auto" isEqualToString:_orient] ? -1 : [_orient doubleValue]; - float angle = 180 + (markerAngle == -1 ? [position angle] : markerAngle); - float rad = deg2rad(angle); - transform = CGAffineTransformRotate(transform, rad); + float markerAngle = [@"auto" isEqualToString:_orient] ? -1 : [_orient doubleValue]; + float angle = 180 + (markerAngle == -1 ? [position angle] : markerAngle); + float rad = deg2rad(angle); + transform = CGAffineTransformRotate(transform, rad); - bool useStrokeWidth = [@"strokeWidth" isEqualToString:_markerUnits]; - if (useStrokeWidth) { - transform = CGAffineTransformScale(transform, strokeWidth, strokeWidth); - } + bool useStrokeWidth = [@"strokeWidth" isEqualToString:_markerUnits]; + if (useStrokeWidth) { + transform = CGAffineTransformScale(transform, strokeWidth, strokeWidth); + } - CGFloat width = [self relativeOnWidth:self.markerWidth]; - CGFloat height = [self relativeOnHeight:self.markerHeight]; - CGRect eRect = CGRectMake(0, 0, width, height); - if (self.align) { - CGAffineTransform viewBoxTransform = [RNSVGViewBox getTransform:CGRectMake(self.minX, self.minY, self.vbWidth, self.vbHeight) - eRect:eRect - align:self.align - meetOrSlice:self.meetOrSlice]; - transform = CGAffineTransformScale(transform, viewBoxTransform.a, viewBoxTransform.d); - } + CGFloat width = [self relativeOnWidth:self.markerWidth]; + CGFloat height = [self relativeOnHeight:self.markerHeight]; + CGRect eRect = CGRectMake(0, 0, width, height); + if (self.align) { + CGAffineTransform viewBoxTransform = + [RNSVGViewBox getTransform:CGRectMake(self.minX, self.minY, self.vbWidth, self.vbHeight) + eRect:eRect + align:self.align + meetOrSlice:self.meetOrSlice]; + transform = CGAffineTransformScale(transform, viewBoxTransform.a, viewBoxTransform.d); + } - CGFloat x = [self relativeOnWidth:self.refX]; - CGFloat y = [self relativeOnHeight:self.refY]; - transform = CGAffineTransformTranslate(transform, -x, -y); + CGFloat x = [self relativeOnWidth:self.refX]; + CGFloat y = [self relativeOnHeight:self.refY]; + transform = CGAffineTransformTranslate(transform, -x, -y); - self.transform = transform; - CGContextConcatCTM(context, transform); + self.transform = transform; + CGContextConcatCTM(context, transform); - [self renderGroupTo:context rect:eRect]; + [self renderGroupTo:context rect:eRect]; - CGContextRestoreGState(context); + CGContextRestoreGState(context); } @end - #ifdef RN_FABRIC_ENABLED Class RNSVGMarkerCls(void) { diff --git a/apple/Elements/RNSVGMask.mm b/apple/Elements/RNSVGMask.mm index cc2fbdec..2cebb1fc 100644 --- a/apple/Elements/RNSVGMask.mm +++ b/apple/Elements/RNSVGMask.mm @@ -6,16 +6,15 @@ * LICENSE file in the root directory of this source tree. */ #import "RNSVGMask.h" -#import "RNSVGPainter.h" #import "RNSVGBrushType.h" #import "RNSVGNode.h" - +#import "RNSVGPainter.h" #ifdef RN_FABRIC_ENABLED #import -#import "RCTFabricComponentsPlugins.h" -#import "RCTConversions.h" #import +#import "RCTConversions.h" +#import "RCTFabricComponentsPlugins.h" #import "RNSVGFabricConversions.h" #endif // RN_FABRIC_ENABLED @@ -42,119 +41,125 @@ using namespace facebook::react; - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps { - const auto &newProps = *std::static_pointer_cast(props); - - self.x = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x)]; - self.y = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y)]; - if (RCTNSStringFromStringNilIfEmpty(newProps.maskheight)) { - self.maskheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.maskheight)]; - } - if (RCTNSStringFromStringNilIfEmpty(newProps.maskwidth)) { - self.maskwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.maskwidth)]; - } - if (RCTNSStringFromStringNilIfEmpty(newProps.height)) { - self.maskheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.height)]; - } - if (RCTNSStringFromStringNilIfEmpty(newProps.width)) { - self.maskwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.width)]; - } - self.maskUnits = newProps.maskUnits == 0 ? kRNSVGUnitsObjectBoundingBox : kRNSVGUnitsUserSpaceOnUse; - self.maskContentUnits = newProps.maskUnits == 0 ? kRNSVGUnitsObjectBoundingBox : kRNSVGUnitsUserSpaceOnUse; - if (newProps.maskTransform.size() == 6) { - self.maskTransform = CGAffineTransformMake(newProps.maskTransform.at(0), newProps.maskTransform.at(1), newProps.maskTransform.at(2), newProps.maskTransform.at(3), newProps.maskTransform.at(4), newProps.maskTransform.at(5)); - } - - setCommonGroupProps(newProps, self); + const auto &newProps = *std::static_pointer_cast(props); + + self.x = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x)]; + self.y = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y)]; + if (RCTNSStringFromStringNilIfEmpty(newProps.maskheight)) { + self.maskheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.maskheight)]; + } + if (RCTNSStringFromStringNilIfEmpty(newProps.maskwidth)) { + self.maskwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.maskwidth)]; + } + if (RCTNSStringFromStringNilIfEmpty(newProps.height)) { + self.maskheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.height)]; + } + if (RCTNSStringFromStringNilIfEmpty(newProps.width)) { + self.maskwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.width)]; + } + self.maskUnits = newProps.maskUnits == 0 ? kRNSVGUnitsObjectBoundingBox : kRNSVGUnitsUserSpaceOnUse; + self.maskContentUnits = newProps.maskUnits == 0 ? kRNSVGUnitsObjectBoundingBox : kRNSVGUnitsUserSpaceOnUse; + if (newProps.maskTransform.size() == 6) { + self.maskTransform = CGAffineTransformMake( + newProps.maskTransform.at(0), + newProps.maskTransform.at(1), + newProps.maskTransform.at(2), + newProps.maskTransform.at(3), + newProps.maskTransform.at(4), + newProps.maskTransform.at(5)); + } + + setCommonGroupProps(newProps, self); } - (void)prepareForRecycle { - [super prepareForRecycle]; - _x = nil; - _y = nil; - _maskheight = nil; - _maskwidth = nil; - _maskUnits = kRNSVGUnitsObjectBoundingBox; - _maskContentUnits = kRNSVGUnitsObjectBoundingBox; - _maskTransform = CGAffineTransformIdentity; + [super prepareForRecycle]; + _x = nil; + _y = nil; + _maskheight = nil; + _maskwidth = nil; + _maskUnits = kRNSVGUnitsObjectBoundingBox; + _maskContentUnits = kRNSVGUnitsObjectBoundingBox; + _maskTransform = CGAffineTransformIdentity; } #endif // RN_FABRIC_ENABLED - (RNSVGPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { - return nil; + return nil; } - (void)parseReference { - self.dirty = false; - [self.svgView defineMask:self maskName:self.name]; + self.dirty = false; + [self.svgView defineMask:self maskName:self.name]; } - (void)setX:(RNSVGLength *)x { - if ([x isEqualTo:_x]) { - return; - } - - _x = x; - [self invalidate]; + if ([x isEqualTo:_x]) { + return; + } + + _x = x; + [self invalidate]; } - (void)setY:(RNSVGLength *)y { - if ([y isEqualTo:_y]) { - return; - } - - _y = y; - [self invalidate]; + if ([y isEqualTo:_y]) { + return; + } + + _y = y; + [self invalidate]; } - (void)setMaskwidth:(RNSVGLength *)maskwidth { - if ([maskwidth isEqualTo:_maskwidth]) { - return; - } - - _maskwidth = maskwidth; - [self invalidate]; + if ([maskwidth isEqualTo:_maskwidth]) { + return; + } + + _maskwidth = maskwidth; + [self invalidate]; } - (void)setMaskheight:(RNSVGLength *)maskheight { - if ([maskheight isEqualTo:_maskheight]) { - return; - } - - _maskheight = maskheight; - [self invalidate]; + if ([maskheight isEqualTo:_maskheight]) { + return; + } + + _maskheight = maskheight; + [self invalidate]; } - (void)setMaskUnits:(RNSVGUnits)maskUnits { - if (maskUnits == _maskUnits) { - return; - } - - _maskUnits = maskUnits; - [self invalidate]; + if (maskUnits == _maskUnits) { + return; + } + + _maskUnits = maskUnits; + [self invalidate]; } - (void)setMaskContentUnits:(RNSVGUnits)maskContentUnits { - if (maskContentUnits == _maskContentUnits) { - return; - } - - _maskContentUnits = maskContentUnits; - [self invalidate]; + if (maskContentUnits == _maskContentUnits) { + return; + } + + _maskContentUnits = maskContentUnits; + [self invalidate]; } - (void)setMaskTransform:(CGAffineTransform)maskTransform { - _maskTransform = maskTransform; - [self invalidate]; + _maskTransform = maskTransform; + [self invalidate]; } @end diff --git a/apple/Elements/RNSVGPath.mm b/apple/Elements/RNSVGPath.mm index 666bf030..3dceee0a 100644 --- a/apple/Elements/RNSVGPath.mm +++ b/apple/Elements/RNSVGPath.mm @@ -10,15 +10,14 @@ #ifdef RN_FABRIC_ENABLED #import -#import "RCTFabricComponentsPlugins.h" -#import "RCTConversions.h" #import +#import "RCTConversions.h" +#import "RCTFabricComponentsPlugins.h" #import "RNSVGFabricConversions.h" #endif // RN_FABRIC_ENABLED -@implementation RNSVGPath -{ - CGPathRef _path; +@implementation RNSVGPath { + CGPathRef _path; } #ifdef RN_FABRIC_ENABLED @@ -42,43 +41,43 @@ using namespace facebook::react; - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps { - const auto &newProps = *std::static_pointer_cast(props); - self.d = [[RNSVGPathParser alloc] initWithPathString: RCTNSStringFromString(newProps.d)]; - - setCommonRenderableProps(newProps, self); + const auto &newProps = *std::static_pointer_cast(props); + self.d = [[RNSVGPathParser alloc] initWithPathString:RCTNSStringFromString(newProps.d)]; + + setCommonRenderableProps(newProps, self); } - (void)prepareForRecycle { - [super prepareForRecycle]; - if (_path) { - CGPathRelease(_path); - } - _path = nil; - _d = nil; + [super prepareForRecycle]; + if (_path) { + CGPathRelease(_path); + } + _path = nil; + _d = nil; } #endif // RN_FABRIC_ENABLED - (void)setD:(RNSVGPathParser *)d { - if (d == _d) { - return; - } + if (d == _d) { + return; + } - [self invalidate]; - _d = d; - CGPathRelease(_path); - _path = CGPathRetain([d getPath]); + [self invalidate]; + _d = d; + CGPathRelease(_path); + _path = CGPathRetain([d getPath]); } - (CGPathRef)getPath:(CGContextRef)context { - return _path; + return _path; } - (void)dealloc { - CGPathRelease(_path); + CGPathRelease(_path); } @end diff --git a/apple/Elements/RNSVGPattern.mm b/apple/Elements/RNSVGPattern.mm index 0226ddb9..ccc39306 100644 --- a/apple/Elements/RNSVGPattern.mm +++ b/apple/Elements/RNSVGPattern.mm @@ -6,15 +6,15 @@ * LICENSE file in the root directory of this source tree. */ #import "RNSVGPattern.h" -#import "RNSVGPainter.h" #import "RNSVGBrushType.h" #import "RNSVGNode.h" +#import "RNSVGPainter.h" #ifdef RN_FABRIC_ENABLED #import -#import "RCTFabricComponentsPlugins.h" -#import "RCTConversions.h" #import +#import "RCTConversions.h" +#import "RCTFabricComponentsPlugins.h" #import "RNSVGFabricConversions.h" #endif // RN_FABRIC_ENABLED @@ -41,211 +41,217 @@ using namespace facebook::react; - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps { - const auto &newProps = *std::static_pointer_cast(props); + const auto &newProps = *std::static_pointer_cast(props); - self.x = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x)]; - self.y = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y)]; - if (RCTNSStringFromStringNilIfEmpty(newProps.patternheight)) { - self.patternheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.patternheight)]; - } - if (RCTNSStringFromStringNilIfEmpty(newProps.patternwidth)) { - self.patternwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.patternwidth)]; - } - if (RCTNSStringFromStringNilIfEmpty(newProps.height)) { - self.patternheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.height)]; - } - if (RCTNSStringFromStringNilIfEmpty(newProps.width)) { - self.patternwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.width)]; - } - self.patternUnits = newProps.patternUnits == 0 ? kRNSVGUnitsObjectBoundingBox : kRNSVGUnitsUserSpaceOnUse; - self.patternContentUnits = newProps.patternUnits == 0 ? kRNSVGUnitsObjectBoundingBox : kRNSVGUnitsUserSpaceOnUse; - if (newProps.patternTransform.size() == 6) { - self.patternTransform = CGAffineTransformMake(newProps.patternTransform.at(0), newProps.patternTransform.at(1), newProps.patternTransform.at(2), newProps.patternTransform.at(3), newProps.patternTransform.at(4), newProps.patternTransform.at(5)); - } - self.minX = newProps.minX; - self.minY = newProps.minY; - self.vbWidth = newProps.vbWidth; - self.vbHeight = newProps.vbHeight; - self.align = RCTNSStringFromStringNilIfEmpty(newProps.align); - self.meetOrSlice = intToRNSVGVBMOS(newProps.meetOrSlice); - - setCommonGroupProps(newProps, self); + self.x = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x)]; + self.y = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y)]; + if (RCTNSStringFromStringNilIfEmpty(newProps.patternheight)) { + self.patternheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.patternheight)]; + } + if (RCTNSStringFromStringNilIfEmpty(newProps.patternwidth)) { + self.patternwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.patternwidth)]; + } + if (RCTNSStringFromStringNilIfEmpty(newProps.height)) { + self.patternheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.height)]; + } + if (RCTNSStringFromStringNilIfEmpty(newProps.width)) { + self.patternwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.width)]; + } + self.patternUnits = newProps.patternUnits == 0 ? kRNSVGUnitsObjectBoundingBox : kRNSVGUnitsUserSpaceOnUse; + self.patternContentUnits = newProps.patternUnits == 0 ? kRNSVGUnitsObjectBoundingBox : kRNSVGUnitsUserSpaceOnUse; + if (newProps.patternTransform.size() == 6) { + self.patternTransform = CGAffineTransformMake( + newProps.patternTransform.at(0), + newProps.patternTransform.at(1), + newProps.patternTransform.at(2), + newProps.patternTransform.at(3), + newProps.patternTransform.at(4), + newProps.patternTransform.at(5)); + } + self.minX = newProps.minX; + self.minY = newProps.minY; + self.vbWidth = newProps.vbWidth; + self.vbHeight = newProps.vbHeight; + self.align = RCTNSStringFromStringNilIfEmpty(newProps.align); + self.meetOrSlice = intToRNSVGVBMOS(newProps.meetOrSlice); + + setCommonGroupProps(newProps, self); } - (void)prepareForRecycle { - [super prepareForRecycle]; - _x = nil; - _y = nil; - _patternheight = nil; - _patternwidth = nil; - _patternUnits = kRNSVGUnitsObjectBoundingBox; - _patternContentUnits = kRNSVGUnitsObjectBoundingBox; - _patternTransform = CGAffineTransformIdentity; - - _minX = 0; - _minY = 0; - _vbWidth = 0; - _vbHeight = 0; - _align = nil; - _meetOrSlice = kRNSVGVBMOSMeet; + [super prepareForRecycle]; + _x = nil; + _y = nil; + _patternheight = nil; + _patternwidth = nil; + _patternUnits = kRNSVGUnitsObjectBoundingBox; + _patternContentUnits = kRNSVGUnitsObjectBoundingBox; + _patternTransform = CGAffineTransformIdentity; + + _minX = 0; + _minY = 0; + _vbWidth = 0; + _vbHeight = 0; + _align = nil; + _meetOrSlice = kRNSVGVBMOSMeet; } #endif // RN_FABRIC_ENABLED - (instancetype)init { - if (self = [super init]) { - _patternTransform = CGAffineTransformIdentity; - } - return self; + if (self = [super init]) { + _patternTransform = CGAffineTransformIdentity; + } + return self; } - (RNSVGPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { - return nil; + return nil; } - (void)parseReference { - self.dirty = false; - NSArray *points = @[self.x, self.y, self.patternwidth, self.patternheight]; - RNSVGPainter *painter = [[RNSVGPainter alloc] initWithPointsArray:points]; - [painter setUnits:self.patternUnits]; - [painter setContentUnits:self.patternContentUnits]; - [painter setTransform:self.patternTransform]; - [painter setPattern:self]; + self.dirty = false; + NSArray *points = @[ self.x, self.y, self.patternwidth, self.patternheight ]; + RNSVGPainter *painter = [[RNSVGPainter alloc] initWithPointsArray:points]; + [painter setUnits:self.patternUnits]; + [painter setContentUnits:self.patternContentUnits]; + [painter setTransform:self.patternTransform]; + [painter setPattern:self]; - if (self.patternUnits == kRNSVGUnitsUserSpaceOnUse || self.patternContentUnits == kRNSVGUnitsUserSpaceOnUse) { - [painter setUserSpaceBoundingBox:[self.svgView getContextBounds]]; - } + if (self.patternUnits == kRNSVGUnitsUserSpaceOnUse || self.patternContentUnits == kRNSVGUnitsUserSpaceOnUse) { + [painter setUserSpaceBoundingBox:[self.svgView getContextBounds]]; + } - [self.svgView definePainter:painter painterName:self.name]; + [self.svgView definePainter:painter painterName:self.name]; } - (void)setX:(RNSVGLength *)x { - if ([x isEqualTo:_x]) { - return; - } + if ([x isEqualTo:_x]) { + return; + } - _x = x; - [self invalidate]; + _x = x; + [self invalidate]; } - (void)setY:(RNSVGLength *)y { - if ([y isEqualTo:_y]) { - return; - } + if ([y isEqualTo:_y]) { + return; + } - _y = y; - [self invalidate]; + _y = y; + [self invalidate]; } - (void)setPatternwidth:(RNSVGLength *)patternwidth { - if ([patternwidth isEqualTo:_patternwidth]) { - return; - } + if ([patternwidth isEqualTo:_patternwidth]) { + return; + } - _patternwidth = patternwidth; - [self invalidate]; + _patternwidth = patternwidth; + [self invalidate]; } - (void)setPatternheight:(RNSVGLength *)patternheight { - if ([patternheight isEqualTo:_patternheight]) { - return; - } + if ([patternheight isEqualTo:_patternheight]) { + return; + } - _patternheight = patternheight; - [self invalidate]; + _patternheight = patternheight; + [self invalidate]; } - (void)setPatternUnits:(RNSVGUnits)patternUnits { - if (patternUnits == _patternUnits) { - return; - } + if (patternUnits == _patternUnits) { + return; + } - _patternUnits = patternUnits; - [self invalidate]; + _patternUnits = patternUnits; + [self invalidate]; } - (void)setPatternContentUnits:(RNSVGUnits)patternContentUnits { - if (patternContentUnits == _patternContentUnits) { - return; - } + if (patternContentUnits == _patternContentUnits) { + return; + } - _patternContentUnits = patternContentUnits; - [self invalidate]; + _patternContentUnits = patternContentUnits; + [self invalidate]; } - (void)setPatternTransform:(CGAffineTransform)patternTransform { - _patternTransform = patternTransform; - [self invalidate]; + _patternTransform = patternTransform; + [self invalidate]; } - (void)setMinX:(CGFloat)minX { - if (minX == _minX) { - return; - } + if (minX == _minX) { + return; + } - [self invalidate]; - _minX = minX; + [self invalidate]; + _minX = minX; } - (void)setMinY:(CGFloat)minY { - if (minY == _minY) { - return; - } + if (minY == _minY) { + return; + } - [self invalidate]; - _minY = minY; + [self invalidate]; + _minY = minY; } - (void)setVbWidth:(CGFloat)vbWidth { - if (vbWidth == _vbWidth) { - return; - } + if (vbWidth == _vbWidth) { + return; + } - [self invalidate]; - _vbWidth = vbWidth; + [self invalidate]; + _vbWidth = vbWidth; } - (void)setVbHeight:(CGFloat)vbHeight { - if (_vbHeight == vbHeight) { - return; - } + if (_vbHeight == vbHeight) { + return; + } - [self invalidate]; - _vbHeight = vbHeight; + [self invalidate]; + _vbHeight = vbHeight; } - (void)setAlign:(NSString *)align { - if ([align isEqualToString:_align]) { - return; - } + if ([align isEqualToString:_align]) { + return; + } - [self invalidate]; - _align = align; + [self invalidate]; + _align = align; } - (void)setMeetOrSlice:(RNSVGVBMOS)meetOrSlice { - if (meetOrSlice == _meetOrSlice) { - return; - } + if (meetOrSlice == _meetOrSlice) { + return; + } - [self invalidate]; - _meetOrSlice = meetOrSlice; + [self invalidate]; + _meetOrSlice = meetOrSlice; } @end diff --git a/apple/Elements/RNSVGRadialGradient.h b/apple/Elements/RNSVGRadialGradient.h index deb8e03d..82e3ebfe 100644 --- a/apple/Elements/RNSVGRadialGradient.h +++ b/apple/Elements/RNSVGRadialGradient.h @@ -6,8 +6,8 @@ * LICENSE file in the root directory of this source tree. */ -#import "RNSVGNode.h" #import "RNSVGLength.h" +#import "RNSVGNode.h" @interface RNSVGRadialGradient : RNSVGNode diff --git a/apple/Elements/RNSVGRadialGradient.mm b/apple/Elements/RNSVGRadialGradient.mm index 6fc1b4fe..4edf1559 100644 --- a/apple/Elements/RNSVGRadialGradient.mm +++ b/apple/Elements/RNSVGRadialGradient.mm @@ -9,9 +9,9 @@ #ifdef RN_FABRIC_ENABLED #import -#import "RCTFabricComponentsPlugins.h" -#import "RCTConversions.h" #import +#import "RCTConversions.h" +#import "RCTFabricComponentsPlugins.h" #import "RNSVGFabricConversions.h" #endif // RN_FABRIC_ENABLED @@ -38,156 +38,162 @@ using namespace facebook::react; - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps { - const auto &newProps = *std::static_pointer_cast(props); - - self.fx = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.fx)]; - self.fy = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.fy)]; - self.cx = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.cx)]; - self.cy = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.cy)]; - self.rx = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.rx)]; - self.ry = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.ry)]; - if (newProps.gradient.size() > 0) { - NSMutableArray *gradientArray = [NSMutableArray new]; - for (auto number : newProps.gradient) { - [gradientArray addObject:[NSNumber numberWithDouble:number]]; - } - self.gradient = gradientArray; - } - self.gradientUnits = newProps.gradientUnits == 0 ? kRNSVGUnitsObjectBoundingBox : kRNSVGUnitsUserSpaceOnUse; - if (newProps.gradientTransform.size() == 6) { - self.gradientTransform = CGAffineTransformMake(newProps.gradientTransform.at(0), newProps.gradientTransform.at(1), newProps.gradientTransform.at(2), newProps.gradientTransform.at(3), newProps.gradientTransform.at(4), newProps.gradientTransform.at(5)); - } + const auto &newProps = *std::static_pointer_cast(props); - setCommonNodeProps(newProps, self); + self.fx = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.fx)]; + self.fy = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.fy)]; + self.cx = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.cx)]; + self.cy = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.cy)]; + self.rx = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.rx)]; + self.ry = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.ry)]; + if (newProps.gradient.size() > 0) { + NSMutableArray *gradientArray = [NSMutableArray new]; + for (auto number : newProps.gradient) { + [gradientArray addObject:[NSNumber numberWithDouble:number]]; + } + self.gradient = gradientArray; + } + self.gradientUnits = newProps.gradientUnits == 0 ? kRNSVGUnitsObjectBoundingBox : kRNSVGUnitsUserSpaceOnUse; + if (newProps.gradientTransform.size() == 6) { + self.gradientTransform = CGAffineTransformMake( + newProps.gradientTransform.at(0), + newProps.gradientTransform.at(1), + newProps.gradientTransform.at(2), + newProps.gradientTransform.at(3), + newProps.gradientTransform.at(4), + newProps.gradientTransform.at(5)); + } + + setCommonNodeProps(newProps, self); } - (void)prepareForRecycle { - [super prepareForRecycle]; - _fx = nil; - _fy = nil; - _cx = nil; - _cy = nil; - _rx = nil; - _ry = nil; - _gradient = nil; - _gradientUnits = kRNSVGUnitsObjectBoundingBox; - _gradientTransform = CGAffineTransformIdentity; + [super prepareForRecycle]; + _fx = nil; + _fy = nil; + _cx = nil; + _cy = nil; + _rx = nil; + _ry = nil; + _gradient = nil; + _gradientUnits = kRNSVGUnitsObjectBoundingBox; + _gradientTransform = CGAffineTransformIdentity; } #endif // RN_FABRIC_ENABLED - (instancetype)init { - if (self = [super init]) { - _gradientTransform = CGAffineTransformIdentity; - } - return self; + if (self = [super init]) { + _gradientTransform = CGAffineTransformIdentity; + } + return self; } - (void)setFx:(RNSVGLength *)fx { - if ([fx isEqualTo:_fx]) { - return; - } + if ([fx isEqualTo:_fx]) { + return; + } - _fx = fx; - [self invalidate]; + _fx = fx; + [self invalidate]; } - (void)setFy:(RNSVGLength *)fy { - if ([fy isEqualTo:_fy]) { - return; - } + if ([fy isEqualTo:_fy]) { + return; + } - _fy = fy; - [self invalidate]; + _fy = fy; + [self invalidate]; } - (void)setRx:(RNSVGLength *)rx { - if ([rx isEqualTo:_rx]) { - return; - } + if ([rx isEqualTo:_rx]) { + return; + } - _rx = rx; - [self invalidate]; + _rx = rx; + [self invalidate]; } - (void)setRy:(RNSVGLength *)ry { - if ([ry isEqualTo:_ry]) { - return; - } + if ([ry isEqualTo:_ry]) { + return; + } - _ry = ry; - [self invalidate]; + _ry = ry; + [self invalidate]; } - (void)setCx:(RNSVGLength *)cx { - if ([cx isEqualTo:_cx]) { - return; - } + if ([cx isEqualTo:_cx]) { + return; + } - _cx = cx; - [self invalidate]; + _cx = cx; + [self invalidate]; } - (void)setCy:(RNSVGLength *)cy { - if ([cy isEqualTo:_cy]) { - return; - } + if ([cy isEqualTo:_cy]) { + return; + } - _cy = cy; - [self invalidate]; + _cy = cy; + [self invalidate]; } - (void)setGradient:(NSArray *)gradient { - if (gradient == _gradient) { - return; - } + if (gradient == _gradient) { + return; + } - _gradient = gradient; - [self invalidate]; + _gradient = gradient; + [self invalidate]; } - (void)setGradientUnits:(RNSVGUnits)gradientUnits { - if (gradientUnits == _gradientUnits) { - return; - } + if (gradientUnits == _gradientUnits) { + return; + } - _gradientUnits = gradientUnits; - [self invalidate]; + _gradientUnits = gradientUnits; + [self invalidate]; } - (void)setGradientTransform:(CGAffineTransform)gradientTransform { - _gradientTransform = gradientTransform; - [self invalidate]; + _gradientTransform = gradientTransform; + [self invalidate]; } - (RNSVGPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { - return nil; + return nil; } - (void)parseReference { - self.dirty = false; - NSArray *points = @[self.fx, self.fy, self.rx, self.ry, self.cx, self.cy]; - RNSVGPainter *painter = [[RNSVGPainter alloc] initWithPointsArray:points]; - [painter setUnits:self.gradientUnits]; - [painter setTransform:self.gradientTransform]; - [painter setRadialGradientColors:self.gradient]; + self.dirty = false; + NSArray *points = @[ self.fx, self.fy, self.rx, self.ry, self.cx, self.cy ]; + RNSVGPainter *painter = [[RNSVGPainter alloc] initWithPointsArray:points]; + [painter setUnits:self.gradientUnits]; + [painter setTransform:self.gradientTransform]; + [painter setRadialGradientColors:self.gradient]; - if (self.gradientUnits == kRNSVGUnitsUserSpaceOnUse) { - [painter setUserSpaceBoundingBox:[self.svgView getContextBounds]]; - } + if (self.gradientUnits == kRNSVGUnitsUserSpaceOnUse) { + [painter setUserSpaceBoundingBox:[self.svgView getContextBounds]]; + } - [self.svgView definePainter:painter painterName:self.name]; + [self.svgView definePainter:painter painterName:self.name]; } @end diff --git a/apple/Elements/RNSVGSvgView.h b/apple/Elements/RNSVGSvgView.h index e77eb1be..0af63236 100644 --- a/apple/Elements/RNSVGSvgView.h +++ b/apple/Elements/RNSVGSvgView.h @@ -8,8 +8,8 @@ #import "RNSVGUIKit.h" -#import "RNSVGPainter.h" #import "RNSVGContainer.h" +#import "RNSVGPainter.h" #import "RNSVGVBMOS.h" #ifdef RN_FABRIC_ENABLED @@ -20,9 +20,9 @@ @interface RNSVGSvgView : #ifdef RN_FABRIC_ENABLED -RCTViewComponentView + RCTViewComponentView #else -RNSVGView + RNSVGView #endif // RN_FABRIC_ENABLED @property (nonatomic, strong) RNSVGLength *bbWidth; diff --git a/apple/Elements/RNSVGSvgView.mm b/apple/Elements/RNSVGSvgView.mm index 0b6b04fa..49d46133 100644 --- a/apple/Elements/RNSVGSvgView.mm +++ b/apple/Elements/RNSVGSvgView.mm @@ -7,27 +7,26 @@ */ #import "RNSVGSvgView.h" -#import "RNSVGViewBox.h" -#import "RNSVGNode.h" #import +#import "RNSVGNode.h" +#import "RNSVGViewBox.h" #ifdef RN_FABRIC_ENABLED #import -#import "RCTFabricComponentsPlugins.h" -#import "RCTConversions.h" #import +#import "RCTConversions.h" +#import "RCTFabricComponentsPlugins.h" #import "RNSVGFabricConversions.h" #endif // RN_FABRIC_ENABLED -@implementation RNSVGSvgView -{ - NSMutableDictionary *_clipPaths; - NSMutableDictionary *_templates; - NSMutableDictionary *_painters; - NSMutableDictionary *_markers; - NSMutableDictionary *_masks; - CGAffineTransform _invviewBoxTransform; - bool rendered; +@implementation RNSVGSvgView { + NSMutableDictionary *_clipPaths; + NSMutableDictionary *_templates; + NSMutableDictionary *_painters; + NSMutableDictionary *_markers; + NSMutableDictionary *_masks; + CGAffineTransform _invviewBoxTransform; + bool rendered; } #ifdef RN_FABRIC_ENABLED @@ -36,21 +35,21 @@ using namespace facebook::react; - (instancetype)initWithFrame:(CGRect)frame { - if (self = [super initWithFrame:frame]) { + if (self = [super initWithFrame:frame]) { #if !TARGET_OS_OSX // Not available on macOS - // This is necessary to ensure that [self setNeedsDisplay] actually triggers - // a redraw when our parent transitions between hidden and visible. - self.contentMode = UIViewContentModeRedraw; + // This is necessary to ensure that [self setNeedsDisplay] actually triggers + // a redraw when our parent transitions between hidden and visible. + self.contentMode = UIViewContentModeRedraw; #endif // TARGET_OS_OSX - rendered = false; + rendered = false; #ifdef RN_FABRIC_ENABLED - static const auto defaultProps = std::make_shared(); - _props = defaultProps; - // TODO: think if we can do it better - self.opaque = NO; + static const auto defaultProps = std::make_shared(); + _props = defaultProps; + // TODO: think if we can do it better + self.opaque = NO; #endif // RN_FABRIC_ENABLED - } - return self; + } + return self; } #ifdef RN_FABRIC_ENABLED @@ -63,379 +62,374 @@ using namespace facebook::react; - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps { - const auto &newProps = *std::static_pointer_cast(props); - - self.minX = newProps.minX; - self.minY = newProps.minY; - self.vbWidth = newProps.vbWidth; - self.vbHeight = newProps.vbHeight; - self.bbWidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.bbWidth)]; - self.bbHeight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.bbHeight)]; - self.align = RCTNSStringFromStringNilIfEmpty(newProps.align); - self.meetOrSlice = intToRNSVGVBMOS(newProps.meetOrSlice); - if (RCTUIColorFromSharedColor(newProps.tintColor)) { - self.tintColor = RCTUIColorFromSharedColor(newProps.tintColor); - } - if (RCTUIColorFromSharedColor(newProps.color)) { - self.tintColor = RCTUIColorFromSharedColor(newProps.color); - } -} + const auto &newProps = *std::static_pointer_cast(props); + self.minX = newProps.minX; + self.minY = newProps.minY; + self.vbWidth = newProps.vbWidth; + self.vbHeight = newProps.vbHeight; + self.bbWidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.bbWidth)]; + self.bbHeight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.bbHeight)]; + self.align = RCTNSStringFromStringNilIfEmpty(newProps.align); + self.meetOrSlice = intToRNSVGVBMOS(newProps.meetOrSlice); + if (RCTUIColorFromSharedColor(newProps.tintColor)) { + self.tintColor = RCTUIColorFromSharedColor(newProps.tintColor); + } + if (RCTUIColorFromSharedColor(newProps.color)) { + self.tintColor = RCTUIColorFromSharedColor(newProps.color); + } +} - (void)prepareForRecycle { - [super prepareForRecycle]; - _minX = 0; - _minY = 0; - _vbWidth = 0; - _vbHeight = 0; - _bbWidth = 0; - _bbHeight = 0; - _align = nil; - _meetOrSlice = kRNSVGVBMOSMeet; - - _responsible = NO; - _active = NO; - _boundingBox = CGRectZero; - _initialCTM = CGAffineTransformIdentity; - _invInitialCTM = CGAffineTransformIdentity; - _viewBoxTransform = CGAffineTransformIdentity; - - _clipPaths = nil; - _templates = nil; - _painters = nil; - _markers = nil; - _masks = nil; - _invviewBoxTransform = CGAffineTransformIdentity; - rendered = NO; + [super prepareForRecycle]; + _minX = 0; + _minY = 0; + _vbWidth = 0; + _vbHeight = 0; + _bbWidth = 0; + _bbHeight = 0; + _align = nil; + _meetOrSlice = kRNSVGVBMOSMeet; + + _responsible = NO; + _active = NO; + _boundingBox = CGRectZero; + _initialCTM = CGAffineTransformIdentity; + _invInitialCTM = CGAffineTransformIdentity; + _viewBoxTransform = CGAffineTransformIdentity; + + _clipPaths = nil; + _templates = nil; + _painters = nil; + _markers = nil; + _masks = nil; + _invviewBoxTransform = CGAffineTransformIdentity; + rendered = NO; } #endif // RN_FABRIC_ENABLED - (void)insertReactSubview:(RNSVGView *)subview atIndex:(NSInteger)atIndex { - [super insertReactSubview:subview atIndex:atIndex]; - [self insertSubview:subview atIndex:atIndex]; - [self invalidate]; + [super insertReactSubview:subview atIndex:atIndex]; + [self insertSubview:subview atIndex:atIndex]; + [self invalidate]; } - (void)removeReactSubview:(RNSVGView *)subview { - [super removeReactSubview:subview]; - [self invalidate]; + [super removeReactSubview:subview]; + [self invalidate]; } - (void)didUpdateReactSubviews { - // Do nothing, as subviews are inserted by insertReactSubview: + // Do nothing, as subviews are inserted by insertReactSubview: } - (void)clearChildCache { - if (!rendered) { - return; - } - rendered = false; - for (__kindof RNSVGNode *node in self.subviews) { - if ([node isKindOfClass:[RNSVGNode class]]) { - [node clearChildCache]; - } + if (!rendered) { + return; + } + rendered = false; + for (__kindof RNSVGNode *node in self.subviews) { + if ([node isKindOfClass:[RNSVGNode class]]) { + [node clearChildCache]; } + } } - (void)invalidate { - RNSVGPlatformView* parent = self.superview; - if ([parent isKindOfClass:[RNSVGNode class]]) { - if (!rendered) { - return; - } - RNSVGNode* svgNode = (RNSVGNode*)parent; - [svgNode invalidate]; - rendered = false; - return; + RNSVGPlatformView *parent = self.superview; + if ([parent isKindOfClass:[RNSVGNode class]]) { + if (!rendered) { + return; } - [self setNeedsDisplay]; + RNSVGNode *svgNode = (RNSVGNode *)parent; + [svgNode invalidate]; + rendered = false; + return; + } + [self setNeedsDisplay]; } - (void)tintColorDidChange { - [self invalidate]; - [self clearChildCache]; + [self invalidate]; + [self clearChildCache]; } - (void)setMinX:(CGFloat)minX { - if (minX == _minX) { - return; - } + if (minX == _minX) { + return; + } - [self invalidate]; - [self clearChildCache]; - _minX = minX; + [self invalidate]; + [self clearChildCache]; + _minX = minX; } - (void)setMinY:(CGFloat)minY { - if (minY == _minY) { - return; - } + if (minY == _minY) { + return; + } - [self invalidate]; - [self clearChildCache]; - _minY = minY; + [self invalidate]; + [self clearChildCache]; + _minY = minY; } - (void)setVbWidth:(CGFloat)vbWidth { - if (vbWidth == _vbWidth) { - return; - } + if (vbWidth == _vbWidth) { + return; + } - [self invalidate]; - [self clearChildCache]; - _vbWidth = vbWidth; + [self invalidate]; + [self clearChildCache]; + _vbWidth = vbWidth; } - (void)setVbHeight:(CGFloat)vbHeight { - if (_vbHeight == vbHeight) { - return; - } + if (_vbHeight == vbHeight) { + return; + } - [self invalidate]; - [self clearChildCache]; - _vbHeight = vbHeight; + [self invalidate]; + [self clearChildCache]; + _vbHeight = vbHeight; } - (void)setBbWidth:(RNSVGLength *)bbWidth { - if ([bbWidth isEqualTo:_bbWidth]) { - return; - } + if ([bbWidth isEqualTo:_bbWidth]) { + return; + } - [self invalidate]; - [self clearChildCache]; - _bbWidth = bbWidth; + [self invalidate]; + [self clearChildCache]; + _bbWidth = bbWidth; } - (void)setBbHeight:(RNSVGLength *)bbHeight { - if ([bbHeight isEqualTo:_bbHeight]) { - return; - } + if ([bbHeight isEqualTo:_bbHeight]) { + return; + } - [self invalidate]; - [self clearChildCache]; - _bbHeight = bbHeight; + [self invalidate]; + [self clearChildCache]; + _bbHeight = bbHeight; } - (void)setAlign:(NSString *)align { - if ([align isEqualToString:_align]) { - return; - } + if ([align isEqualToString:_align]) { + return; + } - [self invalidate]; - [self clearChildCache]; - _align = align; + [self invalidate]; + [self clearChildCache]; + _align = align; } - (void)setMeetOrSlice:(RNSVGVBMOS)meetOrSlice { - if (meetOrSlice == _meetOrSlice) { - return; - } + if (meetOrSlice == _meetOrSlice) { + return; + } - [self invalidate]; - [self clearChildCache]; - _meetOrSlice = meetOrSlice; + [self invalidate]; + [self clearChildCache]; + _meetOrSlice = meetOrSlice; } -- (void)drawToContext:(CGContextRef)context withRect:(CGRect)rect { - rendered = true; - self.initialCTM = CGContextGetCTM(context); - self.invInitialCTM = CGAffineTransformInvert(self.initialCTM); - if (self.align) { - CGRect tRect = CGRectMake(self.minX, self.minY, self.vbWidth, self.vbHeight); - _viewBoxTransform = [RNSVGViewBox getTransform:tRect - eRect:rect - align:self.align - meetOrSlice:self.meetOrSlice]; - _invviewBoxTransform = CGAffineTransformInvert(_viewBoxTransform); - CGContextConcatCTM(context, _viewBoxTransform); - } else { - _viewBoxTransform = CGAffineTransformIdentity; - _invviewBoxTransform = CGAffineTransformIdentity; - } +- (void)drawToContext:(CGContextRef)context withRect:(CGRect)rect +{ + rendered = true; + self.initialCTM = CGContextGetCTM(context); + self.invInitialCTM = CGAffineTransformInvert(self.initialCTM); + if (self.align) { + CGRect tRect = CGRectMake(self.minX, self.minY, self.vbWidth, self.vbHeight); + _viewBoxTransform = [RNSVGViewBox getTransform:tRect eRect:rect align:self.align meetOrSlice:self.meetOrSlice]; + _invviewBoxTransform = CGAffineTransformInvert(_viewBoxTransform); + CGContextConcatCTM(context, _viewBoxTransform); + } else { + _viewBoxTransform = CGAffineTransformIdentity; + _invviewBoxTransform = CGAffineTransformIdentity; + } - for (RNSVGView *node in self.subviews) { - if ([node isKindOfClass:[RNSVGNode class]]) { - RNSVGNode *svg = (RNSVGNode *)node; - [svg renderTo:context - rect:rect]; - } else { - [node drawRect:rect]; - } + for (RNSVGView *node in self.subviews) { + if ([node isKindOfClass:[RNSVGNode class]]) { + RNSVGNode *svg = (RNSVGNode *)node; + [svg renderTo:context rect:rect]; + } else { + [node drawRect:rect]; } + } } - (void)drawRect:(CGRect)rect { - RNSVGPlatformView* parent = self.superview; - if ([parent isKindOfClass:[RNSVGNode class]]) { - return; + RNSVGPlatformView *parent = self.superview; + if ([parent isKindOfClass:[RNSVGNode class]]) { + return; + } + rendered = true; + _clipPaths = nil; + _templates = nil; + _painters = nil; + _boundingBox = rect; + CGContextRef context = UIGraphicsGetCurrentContext(); + + for (RNSVGPlatformView *node in self.subviews) { + if ([node isKindOfClass:[RNSVGNode class]]) { + RNSVGNode *svg = (RNSVGNode *)node; + if (svg.responsible && !self.responsible) { + self.responsible = YES; + } + + [svg parseReference]; } - rendered = true; - _clipPaths = nil; - _templates = nil; - _painters = nil; - _boundingBox = rect; - CGContextRef context = UIGraphicsGetCurrentContext(); + } - for (RNSVGPlatformView *node in self.subviews) { - if ([node isKindOfClass:[RNSVGNode class]]) { - RNSVGNode *svg = (RNSVGNode *)node; - if (svg.responsible && !self.responsible) { - self.responsible = YES; - } - - [svg parseReference]; - } - } - - [self drawToContext:context withRect:rect]; + [self drawToContext:context withRect:rect]; } - (RNSVGPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { - CGPoint transformed = point; - if (self.align) { - transformed = CGPointApplyAffineTransform(transformed, _invviewBoxTransform); + CGPoint transformed = point; + if (self.align) { + transformed = CGPointApplyAffineTransform(transformed, _invviewBoxTransform); + } + for (RNSVGNode *node in [self.subviews reverseObjectEnumerator]) { + if (![node isKindOfClass:[RNSVGNode class]]) { + continue; } - for (RNSVGNode *node in [self.subviews reverseObjectEnumerator]) { - if (![node isKindOfClass:[RNSVGNode class]]) { - continue; - } - if (event) { - node.active = NO; - } - - RNSVGPlatformView *hitChild = [node hitTest:transformed withEvent:event]; - - if (hitChild) { - node.active = YES; - return (node.responsible || (node != hitChild)) ? hitChild : self; - } + if (event) { + node.active = NO; } - return nil; + + RNSVGPlatformView *hitChild = [node hitTest:transformed withEvent:event]; + + if (hitChild) { + node.active = YES; + return (node.responsible || (node != hitChild)) ? hitChild : self; + } + } + return nil; } - (NSString *)getDataURL { - UIGraphicsBeginImageContextWithOptions(_boundingBox.size, NO, 0); - [self clearChildCache]; - [self drawRect:_boundingBox]; - [self clearChildCache]; - [self invalidate]; - NSData *imageData = UIImagePNGRepresentation(UIGraphicsGetImageFromCurrentImageContext()); - NSString *base64 = [imageData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]; - UIGraphicsEndImageContext(); - return base64; + UIGraphicsBeginImageContextWithOptions(_boundingBox.size, NO, 0); + [self clearChildCache]; + [self drawRect:_boundingBox]; + [self clearChildCache]; + [self invalidate]; + NSData *imageData = UIImagePNGRepresentation(UIGraphicsGetImageFromCurrentImageContext()); + NSString *base64 = [imageData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]; + UIGraphicsEndImageContext(); + return base64; } - (NSString *)getDataURLwithBounds:(CGRect)bounds { - UIGraphicsBeginImageContextWithOptions(bounds.size, NO, 1); - [self clearChildCache]; - [self drawRect:bounds]; - [self clearChildCache]; - [self invalidate]; - NSData *imageData = UIImagePNGRepresentation(UIGraphicsGetImageFromCurrentImageContext()); - NSString *base64 = [imageData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]; - UIGraphicsEndImageContext(); - return base64; + UIGraphicsBeginImageContextWithOptions(bounds.size, NO, 1); + [self clearChildCache]; + [self drawRect:bounds]; + [self clearChildCache]; + [self invalidate]; + NSData *imageData = UIImagePNGRepresentation(UIGraphicsGetImageFromCurrentImageContext()); + NSString *base64 = [imageData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]; + UIGraphicsEndImageContext(); + return base64; } - (void)reactSetInheritedBackgroundColor:(RNSVGColor *)inheritedBackgroundColor { - self.backgroundColor = inheritedBackgroundColor; + self.backgroundColor = inheritedBackgroundColor; } - (void)defineClipPath:(__kindof RNSVGNode *)clipPath clipPathName:(NSString *)clipPathName { - if (!_clipPaths) { - _clipPaths = [[NSMutableDictionary alloc] init]; - } - [_clipPaths setObject:clipPath forKey:clipPathName]; + if (!_clipPaths) { + _clipPaths = [[NSMutableDictionary alloc] init]; + } + [_clipPaths setObject:clipPath forKey:clipPathName]; } - (RNSVGNode *)getDefinedClipPath:(NSString *)clipPathName { - return _clipPaths ? [_clipPaths objectForKey:clipPathName] : nil; + return _clipPaths ? [_clipPaths objectForKey:clipPathName] : nil; } - (void)defineTemplate:(RNSVGNode *)definedTemplate templateName:(NSString *)templateName { - if (!_templates) { - _templates = [[NSMutableDictionary alloc] init]; - } - [_templates setObject:definedTemplate forKey:templateName]; + if (!_templates) { + _templates = [[NSMutableDictionary alloc] init]; + } + [_templates setObject:definedTemplate forKey:templateName]; } - (RNSVGNode *)getDefinedTemplate:(NSString *)templateName { - return _templates ? [_templates objectForKey:templateName] : nil; + return _templates ? [_templates objectForKey:templateName] : nil; } - - (void)definePainter:(RNSVGPainter *)painter painterName:(NSString *)painterName { - if (!_painters) { - _painters = [[NSMutableDictionary alloc] init]; - } - [_painters setObject:painter forKey:painterName]; + if (!_painters) { + _painters = [[NSMutableDictionary alloc] init]; + } + [_painters setObject:painter forKey:painterName]; } - (RNSVGPainter *)getDefinedPainter:(NSString *)painterName; { - return _painters ? [_painters objectForKey:painterName] : nil; + return _painters ? [_painters objectForKey:painterName] : nil; } - (void)defineMarker:(RNSVGNode *)marker markerName:(NSString *)markerName { - if (!_markers) { - _markers = [[NSMutableDictionary alloc] init]; - } - [_markers setObject:marker forKey:markerName]; + if (!_markers) { + _markers = [[NSMutableDictionary alloc] init]; + } + [_markers setObject:marker forKey:markerName]; } - (RNSVGNode *)getDefinedMarker:(NSString *)markerName; { - return _markers ? [_markers objectForKey:markerName] : nil; + return _markers ? [_markers objectForKey:markerName] : nil; } - (void)defineMask:(RNSVGNode *)mask maskName:(NSString *)maskName { - if (!_masks) { - _masks = [[NSMutableDictionary alloc] init]; - } - [_masks setObject:mask forKey:maskName]; + if (!_masks) { + _masks = [[NSMutableDictionary alloc] init]; + } + [_masks setObject:mask forKey:maskName]; } - (RNSVGNode *)getDefinedMask:(NSString *)maskName; { - return _masks ? [_masks objectForKey:maskName] : nil; + return _masks ? [_masks objectForKey:maskName] : nil; } - (CGRect)getContextBounds { - return CGContextGetClipBoundingBox(UIGraphicsGetCurrentContext()); + return CGContextGetClipBoundingBox(UIGraphicsGetCurrentContext()); } - (CGAffineTransform)getViewBoxTransform { - return _viewBoxTransform; + return _viewBoxTransform; } @end diff --git a/apple/Elements/RNSVGSymbol.mm b/apple/Elements/RNSVGSymbol.mm index 3064ff2f..577faeab 100644 --- a/apple/Elements/RNSVGSymbol.mm +++ b/apple/Elements/RNSVGSymbol.mm @@ -10,9 +10,9 @@ #ifdef RN_FABRIC_ENABLED #import -#import "RCTFabricComponentsPlugins.h" -#import "RCTConversions.h" #import +#import "RCTConversions.h" +#import "RCTFabricComponentsPlugins.h" #import "RNSVGFabricConversions.h" #endif // RN_FABRIC_ENABLED @@ -39,110 +39,110 @@ using namespace facebook::react; - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps { - const auto &newProps = *std::static_pointer_cast(props); + const auto &newProps = *std::static_pointer_cast(props); - self.minX = newProps.minX; - self.minY = newProps.minY; - self.vbWidth = newProps.vbWidth; - self.vbHeight = newProps.vbHeight; - self.align = RCTNSStringFromStringNilIfEmpty(newProps.align); - self.meetOrSlice = intToRNSVGVBMOS(newProps.meetOrSlice); - - setCommonGroupProps(newProps, self); + self.minX = newProps.minX; + self.minY = newProps.minY; + self.vbWidth = newProps.vbWidth; + self.vbHeight = newProps.vbHeight; + self.align = RCTNSStringFromStringNilIfEmpty(newProps.align); + self.meetOrSlice = intToRNSVGVBMOS(newProps.meetOrSlice); + + setCommonGroupProps(newProps, self); } - (void)prepareForRecycle { - [super prepareForRecycle]; - - _minX = 0; - _minY = 0; - _vbWidth = 0; - _vbHeight = 0; - _align = nil; - _meetOrSlice = kRNSVGVBMOSMeet; + [super prepareForRecycle]; + + _minX = 0; + _minY = 0; + _vbWidth = 0; + _vbHeight = 0; + _align = nil; + _meetOrSlice = kRNSVGVBMOSMeet; } #endif // RN_FABRIC_ENABLED - (void)setMinX:(CGFloat)minX { - if (minX == _minX) { - return; - } - - [self invalidate]; - _minX = minX; + if (minX == _minX) { + return; + } + + [self invalidate]; + _minX = minX; } - (void)setMinY:(CGFloat)minY { - if (minY == _minY) { - return; - } - - [self invalidate]; - _minY = minY; + if (minY == _minY) { + return; + } + + [self invalidate]; + _minY = minY; } - (void)setVbWidth:(CGFloat)vbWidth { - if (vbWidth == _vbWidth) { - return; - } - - [self invalidate]; - _vbWidth = vbWidth; + if (vbWidth == _vbWidth) { + return; + } + + [self invalidate]; + _vbWidth = vbWidth; } - (void)setVbHeight:(CGFloat)vbHeight { - if (_vbHeight == vbHeight) { - return; - } - - [self invalidate]; - _vbHeight = vbHeight; + if (_vbHeight == vbHeight) { + return; + } + + [self invalidate]; + _vbHeight = vbHeight; } - (void)setAlign:(NSString *)align { - if ([align isEqualToString:_align]) { - return; - } - - [self invalidate]; - _align = align; + if ([align isEqualToString:_align]) { + return; + } + + [self invalidate]; + _align = align; } - (void)setMeetOrSlice:(RNSVGVBMOS)meetOrSlice { - if (meetOrSlice == _meetOrSlice) { - return; - } - - [self invalidate]; - _meetOrSlice = meetOrSlice; + if (meetOrSlice == _meetOrSlice) { + return; + } + + [self invalidate]; + _meetOrSlice = meetOrSlice; } - (void)renderTo:(CGContextRef)context rect:(CGRect)rect { - self.dirty = false; - // Do not render Symbol + self.dirty = false; + // Do not render Symbol } - (void)renderSymbolTo:(CGContextRef)context width:(CGFloat)width height:(CGFloat)height { - CGRect eRect = CGRectMake(0, 0, width, height); - if (self.align) { - - CGAffineTransform viewBoxTransform = [RNSVGViewBox getTransform:CGRectMake(self.minX, self.minY, self.vbWidth, self.vbHeight) - eRect:eRect - align:self.align - meetOrSlice:self.meetOrSlice]; - - CGContextConcatCTM(context, viewBoxTransform); - } - [self renderGroupTo:context rect:eRect]; + CGRect eRect = CGRectMake(0, 0, width, height); + if (self.align) { + CGAffineTransform viewBoxTransform = + [RNSVGViewBox getTransform:CGRectMake(self.minX, self.minY, self.vbWidth, self.vbHeight) + eRect:eRect + align:self.align + meetOrSlice:self.meetOrSlice]; + + CGContextConcatCTM(context, viewBoxTransform); + } + [self renderGroupTo:context rect:eRect]; } @end diff --git a/apple/Elements/RNSVGUse.h b/apple/Elements/RNSVGUse.h index 6d0f3dd7..7c89bbcc 100644 --- a/apple/Elements/RNSVGUse.h +++ b/apple/Elements/RNSVGUse.h @@ -6,8 +6,8 @@ * LICENSE file in the root directory of this source tree. */ -#import "RNSVGRenderable.h" #import "RNSVGLength.h" +#import "RNSVGRenderable.h" /** * RNSVG defination are implemented as abstract UIViews for all elements inside Defs. diff --git a/apple/Elements/RNSVGUse.mm b/apple/Elements/RNSVGUse.mm index a1e21af5..6aeb84ed 100644 --- a/apple/Elements/RNSVGUse.mm +++ b/apple/Elements/RNSVGUse.mm @@ -6,14 +6,14 @@ * LICENSE file in the root directory of this source tree. */ #import "RNSVGUse.h" -#import "RNSVGSymbol.h" #import +#import "RNSVGSymbol.h" #ifdef RN_FABRIC_ENABLED #import -#import "RCTFabricComponentsPlugins.h" -#import "RCTConversions.h" #import +#import "RCTConversions.h" +#import "RCTFabricComponentsPlugins.h" #import "RNSVGFabricConversions.h" #endif // RN_FABRIC_ENABLED @@ -40,166 +40,171 @@ using namespace facebook::react; - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps { - const auto &newProps = *std::static_pointer_cast(props); + const auto &newProps = *std::static_pointer_cast(props); - self.x = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x)]; - self.y = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y)]; - if (RCTNSStringFromStringNilIfEmpty(newProps.useheight)) { - self.useheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.useheight)]; - } - if (RCTNSStringFromStringNilIfEmpty(newProps.usewidth)) { - self.usewidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.usewidth)]; - } - if (RCTNSStringFromStringNilIfEmpty(newProps.height)) { - self.useheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.height)]; - } - if (RCTNSStringFromStringNilIfEmpty(newProps.width)) { - self.usewidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.width)]; - } - self.href = RCTNSStringFromStringNilIfEmpty(newProps.href); + self.x = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x)]; + self.y = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y)]; + if (RCTNSStringFromStringNilIfEmpty(newProps.useheight)) { + self.useheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.useheight)]; + } + if (RCTNSStringFromStringNilIfEmpty(newProps.usewidth)) { + self.usewidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.usewidth)]; + } + if (RCTNSStringFromStringNilIfEmpty(newProps.height)) { + self.useheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.height)]; + } + if (RCTNSStringFromStringNilIfEmpty(newProps.width)) { + self.usewidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.width)]; + } + self.href = RCTNSStringFromStringNilIfEmpty(newProps.href); - setCommonRenderableProps(newProps, self); + setCommonRenderableProps(newProps, self); } - (void)prepareForRecycle { - [super prepareForRecycle]; - _x = nil; - _y = nil; - _useheight = nil; - _usewidth = nil; - _href = nil; + [super prepareForRecycle]; + _x = nil; + _y = nil; + _useheight = nil; + _usewidth = nil; + _href = nil; } #endif // RN_FABRIC_ENABLED - (void)setHref:(NSString *)href { - if ([href isEqualToString:_href]) { - return; - } + if ([href isEqualToString:_href]) { + return; + } - [self invalidate]; - _href = href; + [self invalidate]; + _href = href; } - (void)setX:(RNSVGLength *)x { - if ([x isEqualTo:_x]) { - return; - } + if ([x isEqualTo:_x]) { + return; + } - [self invalidate]; - _x = x; + [self invalidate]; + _x = x; } - (void)setY:(RNSVGLength *)y { - if ([y isEqualTo:_y]) { - return; - } + if ([y isEqualTo:_y]) { + return; + } - [self invalidate]; - _y = y; + [self invalidate]; + _y = y; } - - (void)setUsewidth:(RNSVGLength *)usewidth { - if ([usewidth isEqualTo:_usewidth]) { - return; - } + if ([usewidth isEqualTo:_usewidth]) { + return; + } - [self invalidate]; - _usewidth = usewidth; + [self invalidate]; + _usewidth = usewidth; } - (void)setUseheight:(RNSVGLength *)useheight { - if ([useheight isEqualTo:_useheight]) { - return; - } + if ([useheight isEqualTo:_useheight]) { + return; + } - [self invalidate]; - _useheight = useheight; + [self invalidate]; + _useheight = useheight; } - (void)renderLayerTo:(CGContextRef)context rect:(CGRect)rect { - CGContextTranslateCTM(context, [self relativeOnWidth:self.x], [self relativeOnHeight:self.y]); - RNSVGNode* definedTemplate = [self.svgView getDefinedTemplate:self.href]; - if (definedTemplate) { - [self beginTransparencyLayer:context]; - [self clip:context]; + CGContextTranslateCTM(context, [self relativeOnWidth:self.x], [self relativeOnHeight:self.y]); + RNSVGNode *definedTemplate = [self.svgView getDefinedTemplate:self.href]; + if (definedTemplate) { + [self beginTransparencyLayer:context]; + [self clip:context]; - if ([definedTemplate isKindOfClass:[RNSVGRenderable class]]) { - [(RNSVGRenderable*)definedTemplate mergeProperties:self]; - } + if ([definedTemplate isKindOfClass:[RNSVGRenderable class]]) { + [(RNSVGRenderable *)definedTemplate mergeProperties:self]; + } - if ([definedTemplate class] == [RNSVGSymbol class]) { - RNSVGSymbol *symbol = (RNSVGSymbol*)definedTemplate; - [symbol renderSymbolTo:context width:[self relativeOnWidth:self.usewidth] height:[self relativeOnHeight:self.useheight]]; - } else { - [definedTemplate renderTo:context rect:rect]; - } - - if ([definedTemplate isKindOfClass:[RNSVGRenderable class]]) { - [(RNSVGRenderable*)definedTemplate resetProperties]; - } - - [self endTransparencyLayer:context]; - } else if (self.href) { - // TODO: calling yellow box here - RCTLogWarn(@"`Use` element expected a pre-defined svg template as `href` prop, template named: %@ is not defined.", self.href); - return; + if ([definedTemplate class] == [RNSVGSymbol class]) { + RNSVGSymbol *symbol = (RNSVGSymbol *)definedTemplate; + [symbol renderSymbolTo:context + width:[self relativeOnWidth:self.usewidth] + height:[self relativeOnHeight:self.useheight]]; } else { - return; + [definedTemplate renderTo:context rect:rect]; } - CGRect bounds = definedTemplate.clientRect; - self.clientRect = bounds; - - CGAffineTransform current = CGContextGetCTM(context); - CGAffineTransform svgToClientTransform = CGAffineTransformConcat(current, self.svgView.invInitialCTM); - - self.ctm = svgToClientTransform; - self.screenCTM = current; - - CGAffineTransform transform = CGAffineTransformConcat(self.matrix, self.transforms); - CGPoint mid = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds)); - CGPoint center = CGPointApplyAffineTransform(mid, transform); - self.bounds = bounds; - if (!isnan(center.x) && !isnan(center.y)) { - self.center = center; + if ([definedTemplate isKindOfClass:[RNSVGRenderable class]]) { + [(RNSVGRenderable *)definedTemplate resetProperties]; } - self.frame = bounds; + + [self endTransparencyLayer:context]; + } else if (self.href) { + // TODO: calling yellow box here + RCTLogWarn( + @"`Use` element expected a pre-defined svg template as `href` prop, template named: %@ is not defined.", + self.href); + return; + } else { + return; + } + CGRect bounds = definedTemplate.clientRect; + self.clientRect = bounds; + + CGAffineTransform current = CGContextGetCTM(context); + CGAffineTransform svgToClientTransform = CGAffineTransformConcat(current, self.svgView.invInitialCTM); + + self.ctm = svgToClientTransform; + self.screenCTM = current; + + CGAffineTransform transform = CGAffineTransformConcat(self.matrix, self.transforms); + CGPoint mid = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds)); + CGPoint center = CGPointApplyAffineTransform(mid, transform); + + self.bounds = bounds; + if (!isnan(center.x) && !isnan(center.y)) { + self.center = center; + } + self.frame = bounds; } -- (RNSVGPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { - CGPoint transformed = CGPointApplyAffineTransform(point, self.invmatrix); - transformed = CGPointApplyAffineTransform(transformed, self.invTransform); - RNSVGNode const* definedTemplate = [self.svgView getDefinedTemplate:self.href]; - if (event) { - self.active = NO; - } else if (self.active) { - return self; - } - RNSVGPlatformView const* hitChild = [definedTemplate hitTest:transformed withEvent:event]; - if (hitChild) { - self.active = YES; - return self; - } - return nil; -} - -- (CGPathRef)getPath: (CGContextRef)context +- (RNSVGPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { - CGAffineTransform transform = CGAffineTransformMakeTranslation([self relativeOnWidth:self.x], [self relativeOnHeight:self.y]); - RNSVGNode const* definedTemplate = [self.svgView getDefinedTemplate:self.href]; - if (!definedTemplate) { - return nil; - } - CGPathRef path = [definedTemplate getPath:context]; - return CGPathCreateCopyByTransformingPath(path, &transform); + CGPoint transformed = CGPointApplyAffineTransform(point, self.invmatrix); + transformed = CGPointApplyAffineTransform(transformed, self.invTransform); + RNSVGNode const *definedTemplate = [self.svgView getDefinedTemplate:self.href]; + if (event) { + self.active = NO; + } else if (self.active) { + return self; + } + RNSVGPlatformView const *hitChild = [definedTemplate hitTest:transformed withEvent:event]; + if (hitChild) { + self.active = YES; + return self; + } + return nil; +} + +- (CGPathRef)getPath:(CGContextRef)context +{ + CGAffineTransform transform = + CGAffineTransformMakeTranslation([self relativeOnWidth:self.x], [self relativeOnHeight:self.y]); + RNSVGNode const *definedTemplate = [self.svgView getDefinedTemplate:self.href]; + if (!definedTemplate) { + return nil; + } + CGPathRef path = [definedTemplate getPath:context]; + return CGPathCreateCopyByTransformingPath(path, &transform); } @end diff --git a/apple/RNSVGNode.h b/apple/RNSVGNode.h index 17b5bf79..e5e9d729 100644 --- a/apple/RNSVGNode.h +++ b/apple/RNSVGNode.h @@ -6,10 +6,11 @@ * LICENSE file in the root directory of this source tree. */ -#import "RNSVGSvgView.h" -#import -#import #import "RNSVGCGFCRule.h" +#import "RNSVGSvgView.h" + +#import +#import #ifdef RN_FABRIC_ENABLED #import @@ -24,9 +25,9 @@ @interface RNSVGNode : #ifdef RN_FABRIC_ENABLED -RCTViewComponentView + RCTViewComponentView #else -RNSVGView + RNSVGView #endif // RN_FABRIC_ENABLED /* N[1/Sqrt[2], 36] @@ -73,7 +74,6 @@ extern CGFloat const RNSVG_DEFAULT_FONT_SIZE; @property (nonatomic, assign) CGRect markerBounds; @property (nonatomic, copy) RCTDirectEventBlock onLayout; - /** * RNSVGSvgView which ownes current RNSVGNode */ @@ -111,7 +111,7 @@ extern CGFloat const RNSVG_DEFAULT_FONT_SIZE; /** * getPath will return the path inside node as a ClipPath. */ -- (CGPathRef)getPath:(CGContextRef) context; +- (CGPathRef)getPath:(CGContextRef)context; - (CGFloat)relativeOnWidthString:(NSString *)length; diff --git a/apple/RNSVGNode.mm b/apple/RNSVGNode.mm index 55e792d3..a399bfe3 100644 --- a/apple/RNSVGNode.mm +++ b/apple/RNSVGNode.mm @@ -7,25 +7,24 @@ */ #import "RNSVGNode.h" -#import "RNSVGContainer.h" #import "RNSVGClipPath.h" -#import "RNSVGGroup.h" +#import "RNSVGContainer.h" #import "RNSVGGlyphContext.h" +#import "RNSVGGroup.h" -@interface RNSVGNode() +@interface RNSVGNode () @property (nonatomic, readwrite, weak) RNSVGSvgView *svgView; @property (nonatomic, readwrite, weak) RNSVGGroup *textRoot; @end -@implementation RNSVGNode -{ - RNSVGGlyphContext *glyphContext; - BOOL _transparent; - RNSVGClipPath *_clipNode; - CGPathRef _cachedClipPath; - CGFloat canvasWidth; - CGFloat canvasHeight; - CGFloat canvasDiagonal; +@implementation RNSVGNode { + RNSVGGlyphContext *glyphContext; + BOOL _transparent; + RNSVGClipPath *_clipNode; + CGPathRef _cachedClipPath; + CGFloat canvasWidth; + CGFloat canvasHeight; + CGFloat canvasDiagonal; } CGFloat const RNSVG_M_SQRT1_2l = (CGFloat)0.707106781186547524400844362104849039; @@ -33,139 +32,139 @@ CGFloat const RNSVG_DEFAULT_FONT_SIZE = 12; - (instancetype)init { - if (self = [super init]) { - self.opacity = 1; - self.opaque = false; - self.matrix = CGAffineTransformIdentity; - self.transforms = CGAffineTransformIdentity; - self.invTransform = CGAffineTransformIdentity; - _merging = false; - _dirty = false; - } - return self; + if (self = [super init]) { + self.opacity = 1; + self.opaque = false; + self.matrix = CGAffineTransformIdentity; + self.transforms = CGAffineTransformIdentity; + self.invTransform = CGAffineTransformIdentity; + _merging = false; + _dirty = false; + } + return self; } - (void)insertReactSubview:(RNSVGView *)subview atIndex:(NSInteger)atIndex { - [super insertReactSubview:subview atIndex:atIndex]; - [self insertSubview:subview atIndex:atIndex]; - [self invalidate]; + [super insertReactSubview:subview atIndex:atIndex]; + [self insertSubview:subview atIndex:atIndex]; + [self invalidate]; } - (void)removeReactSubview:(RNSVGView *)subview { - [super removeReactSubview:subview]; - [self invalidate]; + [super removeReactSubview:subview]; + [self invalidate]; } - (void)didUpdateReactSubviews { - // Do nothing, as subviews are inserted by insertReactSubview: + // Do nothing, as subviews are inserted by insertReactSubview: } - (void)invalidate { - if (_dirty || _merging) { - return; - } - _dirty = true; - RNSVGView *container = self.superview; - // on Fabric, when the child components are added to hierarchy and their props are set, - // their superview is not set yet. - if ([container conformsToProtocol:@protocol(RNSVGContainer)]){ - [(id)container invalidate]; - } - [self clearPath]; - canvasWidth = -1; - canvasHeight = -1; - canvasDiagonal = -1; + if (_dirty || _merging) { + return; + } + _dirty = true; + RNSVGView *container = self.superview; + // on Fabric, when the child components are added to hierarchy and their props are set, + // their superview is not set yet. + if ([container conformsToProtocol:@protocol(RNSVGContainer)]) { + [(id)container invalidate]; + } + [self clearPath]; + canvasWidth = -1; + canvasHeight = -1; + canvasDiagonal = -1; } - (void)clearPath { - CGPathRelease(_path); - self.path = nil; + CGPathRelease(_path); + self.path = nil; } - (void)clearChildCache { - [self clearPath]; - for (__kindof RNSVGNode *node in self.subviews) { - if ([node isKindOfClass:[RNSVGNode class]]) { - [node clearChildCache]; - } + [self clearPath]; + for (__kindof RNSVGNode *node in self.subviews) { + if ([node isKindOfClass:[RNSVGNode class]]) { + [node clearChildCache]; } + } } - (void)clearParentCache { - RNSVGNode* node = self; - while (node != nil) { - RNSVGPlatformView* parent = [node superview]; + RNSVGNode *node = self; + while (node != nil) { + RNSVGPlatformView *parent = [node superview]; - if (![parent isKindOfClass:[RNSVGNode class]]) { - return; - } - node = (RNSVGNode*)parent; - if (!node.path) { - return; - } - [node clearPath]; + if (![parent isKindOfClass:[RNSVGNode class]]) { + return; } + node = (RNSVGNode *)parent; + if (!node.path) { + return; + } + [node clearPath]; + } } - (RNSVGGroup *)textRoot { - if (_textRoot) { - return _textRoot; - } - - RNSVGNode* node = self; - while (node != nil) { - if ([node isKindOfClass:[RNSVGGroup class]] && [((RNSVGGroup*) node) getGlyphContext] != nil) { - _textRoot = (RNSVGGroup*)node; - break; - } - - RNSVGPlatformView* parent = [node superview]; - - if (![node isKindOfClass:[RNSVGNode class]]) { - node = nil; - } else { - node = (RNSVGNode*)parent; - } - } - + if (_textRoot) { return _textRoot; + } + + RNSVGNode *node = self; + while (node != nil) { + if ([node isKindOfClass:[RNSVGGroup class]] && [((RNSVGGroup *)node) getGlyphContext] != nil) { + _textRoot = (RNSVGGroup *)node; + break; + } + + RNSVGPlatformView *parent = [node superview]; + + if (![node isKindOfClass:[RNSVGNode class]]) { + node = nil; + } else { + node = (RNSVGNode *)parent; + } + } + + return _textRoot; } - (RNSVGGroup *)getParentTextRoot { - RNSVGNode* parent = (RNSVGGroup*)[self superview]; - if (![parent isKindOfClass:[RNSVGGroup class]]) { - return nil; - } else { - return parent.textRoot; - } + RNSVGNode *parent = (RNSVGGroup *)[self superview]; + if (![parent isKindOfClass:[RNSVGGroup class]]) { + return nil; + } else { + return parent.textRoot; + } } - (CGFloat)getFontSizeFromContext { - RNSVGGroup* root = self.textRoot; - if (root == nil) { - return RNSVG_DEFAULT_FONT_SIZE; - } + RNSVGGroup *root = self.textRoot; + if (root == nil) { + return RNSVG_DEFAULT_FONT_SIZE; + } - if (glyphContext == nil) { - glyphContext = [root getGlyphContext]; - } + if (glyphContext == nil) { + glyphContext = [root getGlyphContext]; + } - return [glyphContext getFontSize]; + return [glyphContext getFontSize]; } - (void)reactSetInheritedBackgroundColor:(RNSVGColor *)inheritedBackgroundColor { - self.backgroundColor = inheritedBackgroundColor; + self.backgroundColor = inheritedBackgroundColor; } - (void)setPointerEvents:(RCTPointerEvents)pointerEvents @@ -183,472 +182,475 @@ CGFloat const RNSVG_DEFAULT_FONT_SIZE = 12; - (void)setName:(NSString *)name { - if ([name isEqualToString:_name]) { - return; - } + if ([name isEqualToString:_name]) { + return; + } - [self invalidate]; - _name = name; + [self invalidate]; + _name = name; } - (void)setDisplay:(NSString *)display { - if ([display isEqualToString:_display]) { - return; - } + if ([display isEqualToString:_display]) { + return; + } - [self invalidate]; - _display = display; + [self invalidate]; + _display = display; } - (void)setOpacity:(CGFloat)opacity { - if (opacity == _opacity) { - return; - } + if (opacity == _opacity) { + return; + } - if (opacity <= 0) { - opacity = 0; - } else if (opacity > 1) { - opacity = 1; - } + if (opacity <= 0) { + opacity = 0; + } else if (opacity > 1) { + opacity = 1; + } - [self invalidate]; - _transparent = opacity < 1; - _opacity = opacity; + [self invalidate]; + _transparent = opacity < 1; + _opacity = opacity; } - (void)setMatrix:(CGAffineTransform)matrix { - if (CGAffineTransformEqualToTransform(matrix, _matrix)) { - return; - } - _matrix = matrix; - _invmatrix = CGAffineTransformInvert(matrix); - RNSVGView *container = self.superview; - // on Fabric, when the child components are added to hierarchy and their props are set, - // their superview is still their componentView, we change it in `mountChildComponentView` method. - if ([container conformsToProtocol:@protocol(RNSVGContainer)]){ - [(id)container invalidate]; - } + if (CGAffineTransformEqualToTransform(matrix, _matrix)) { + return; + } + _matrix = matrix; + _invmatrix = CGAffineTransformInvert(matrix); + RNSVGView *container = self.superview; + // on Fabric, when the child components are added to hierarchy and their props are set, + // their superview is still their componentView, we change it in `mountChildComponentView` method. + if ([container conformsToProtocol:@protocol(RNSVGContainer)]) { + [(id)container invalidate]; + } } -- (void)setClientRect:(CGRect)clientRect { - if (CGRectEqualToRect(_clientRect, clientRect)) { - return; - } - _clientRect = clientRect; - if (self.onLayout) { - self.onLayout(@{ - @"layout": @{ - @"x": @(_clientRect.origin.x), - @"y": @(_clientRect.origin.y), - @"width": @(_clientRect.size.width), - @"height": @(_clientRect.size.height), - } - }); - - } +- (void)setClientRect:(CGRect)clientRect +{ + if (CGRectEqualToRect(_clientRect, clientRect)) { + return; + } + _clientRect = clientRect; + if (self.onLayout) { + self.onLayout(@{ + @"layout" : @{ + @"x" : @(_clientRect.origin.x), + @"y" : @(_clientRect.origin.y), + @"width" : @(_clientRect.size.width), + @"height" : @(_clientRect.size.height), + } + }); + } } - (void)setClipPath:(NSString *)clipPath { - if ([_clipPath isEqualToString:clipPath]) { - return; - } - CGPathRelease(_cachedClipPath); - _cachedClipPath = nil; - _clipPath = clipPath; - [self invalidate]; + if ([_clipPath isEqualToString:clipPath]) { + return; + } + CGPathRelease(_cachedClipPath); + _cachedClipPath = nil; + _clipPath = clipPath; + [self invalidate]; } - (void)setClipRule:(RNSVGCGFCRule)clipRule { - if (_clipRule == clipRule) { - return; - } - CGPathRelease(_cachedClipPath); - _cachedClipPath = nil; - _clipRule = clipRule; - [self invalidate]; + if (_clipRule == clipRule) { + return; + } + CGPathRelease(_cachedClipPath); + _cachedClipPath = nil; + _clipRule = clipRule; + [self invalidate]; } - (void)setMask:(NSString *)mask { - if ([_mask isEqualToString:mask]) { - return; - } - _mask = mask; - [self invalidate]; + if ([_mask isEqualToString:mask]) { + return; + } + _mask = mask; + [self invalidate]; } - (void)setMarkerStart:(NSString *)markerStart { - if ([_markerStart isEqualToString:markerStart]) { - return; - } - _markerStart = markerStart; - [self invalidate]; + if ([_markerStart isEqualToString:markerStart]) { + return; + } + _markerStart = markerStart; + [self invalidate]; } - (void)setMarkerMid:(NSString *)markerMid { - if ([_markerMid isEqualToString:markerMid]) { - return; - } - _markerMid = markerMid; - [self invalidate]; + if ([_markerMid isEqualToString:markerMid]) { + return; + } + _markerMid = markerMid; + [self invalidate]; } - (void)setMarkerEnd:(NSString *)markerEnd { - if ([_markerEnd isEqualToString:markerEnd]) { - return; - } - _markerEnd = markerEnd; - [self invalidate]; + if ([_markerEnd isEqualToString:markerEnd]) { + return; + } + _markerEnd = markerEnd; + [self invalidate]; } - (void)beginTransparencyLayer:(CGContextRef)context { - if (_transparent) { - CGContextBeginTransparencyLayer(context, NULL); - } + if (_transparent) { + CGContextBeginTransparencyLayer(context, NULL); + } } - (void)endTransparencyLayer:(CGContextRef)context { - if (_transparent) { - CGContextEndTransparencyLayer(context); - } + if (_transparent) { + CGContextEndTransparencyLayer(context); + } } - (void)renderTo:(CGContextRef)context rect:(CGRect)rect { - self.dirty = false; - // abstract + self.dirty = false; + // abstract } - (CGPathRef)getClipPath { - return _cachedClipPath; + return _cachedClipPath; } - (CGPathRef)getClipPath:(CGContextRef)context { - if (self.clipPath) { - _clipNode = (RNSVGClipPath*)[self.svgView getDefinedClipPath:self.clipPath]; - if (_cachedClipPath) { - CGPathRelease(_cachedClipPath); - } - CGAffineTransform transform = CGAffineTransformConcat(_clipNode.matrix, _clipNode.transforms); - _cachedClipPath = CGPathCreateCopyByTransformingPath([_clipNode getPath:context], &transform); + if (self.clipPath) { + _clipNode = (RNSVGClipPath *)[self.svgView getDefinedClipPath:self.clipPath]; + if (_cachedClipPath) { + CGPathRelease(_cachedClipPath); } + CGAffineTransform transform = CGAffineTransformConcat(_clipNode.matrix, _clipNode.transforms); + _cachedClipPath = CGPathCreateCopyByTransformingPath([_clipNode getPath:context], &transform); + } - return _cachedClipPath; + return _cachedClipPath; } - (void)clip:(CGContextRef)context { - CGPathRef clipPath = [self getClipPath:context]; + CGPathRef clipPath = [self getClipPath:context]; - if (clipPath) { - CGContextAddPath(context, clipPath); - if (_clipRule == kRNSVGCGFCRuleEvenodd) { - CGContextEOClip(context); - } else { - CGContextClip(context); - } + if (clipPath) { + CGContextAddPath(context, clipPath); + if (_clipRule == kRNSVGCGFCRuleEvenodd) { + CGContextEOClip(context); + } else { + CGContextClip(context); } + } } -- (CGPathRef)getPath: (CGContextRef)context +- (CGPathRef)getPath:(CGContextRef)context { - // abstract - return nil; + // abstract + return nil; } - (void)renderLayerTo:(CGContextRef)context rect:(CGRect)rect { - // abstract + // abstract } // hitTest delagate - (RNSVGPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { - - // abstract - return nil; + // abstract + return nil; } - (RNSVGSvgView *)svgView { - if (_svgView) { - return _svgView; - } - - __kindof RNSVGPlatformView *parent = self.superview; - - if ([parent class] == [RNSVGSvgView class]) { - _svgView = parent; - } else if ([parent isKindOfClass:[RNSVGNode class]]) { - _svgView = ((RNSVGNode *)parent).svgView; - } else { - RCTLogError(@"RNSVG: %@ should be descendant of a SvgViewShadow.", NSStringFromClass(self.class)); - } - + if (_svgView) { return _svgView; + } + + __kindof RNSVGPlatformView *parent = self.superview; + + if ([parent class] == [RNSVGSvgView class]) { + _svgView = parent; + } else if ([parent isKindOfClass:[RNSVGNode class]]) { + _svgView = ((RNSVGNode *)parent).svgView; + } else { + RCTLogError(@"RNSVG: %@ should be descendant of a SvgViewShadow.", NSStringFromClass(self.class)); + } + + return _svgView; } - (CGFloat)relativeOnWidthString:(NSString *)length { - return [RNSVGPropHelper fromRelativeWithNSString:length - relative:[self getCanvasWidth] - fontSize:[self getFontSizeFromContext]]; + return [RNSVGPropHelper fromRelativeWithNSString:length + relative:[self getCanvasWidth] + fontSize:[self getFontSizeFromContext]]; } - (CGFloat)relativeOnHeightString:(NSString *)length { - return [RNSVGPropHelper fromRelativeWithNSString:length - relative:[self getCanvasHeight] - fontSize:[self getFontSizeFromContext]]; + return [RNSVGPropHelper fromRelativeWithNSString:length + relative:[self getCanvasHeight] + fontSize:[self getFontSizeFromContext]]; } - (CGFloat)relativeOnOtherString:(NSString *)length { - return [RNSVGPropHelper fromRelativeWithNSString:length - relative:[self getCanvasDiagonal] - fontSize:[self getFontSizeFromContext]]; + return [RNSVGPropHelper fromRelativeWithNSString:length + relative:[self getCanvasDiagonal] + fontSize:[self getFontSizeFromContext]]; } - (CGFloat)relativeOn:(RNSVGLength *)length relative:(CGFloat)relative { - RNSVGLengthUnitType unit = length.unit; - if (unit == SVG_LENGTHTYPE_NUMBER){ - return length.value; - } else if (unit == SVG_LENGTHTYPE_PERCENTAGE){ - return length.value / 100 * relative; - } - return [self fromRelative:length]; + RNSVGLengthUnitType unit = length.unit; + if (unit == SVG_LENGTHTYPE_NUMBER) { + return length.value; + } else if (unit == SVG_LENGTHTYPE_PERCENTAGE) { + return length.value / 100 * relative; + } + return [self fromRelative:length]; } - (CGFloat)relativeOnWidth:(RNSVGLength *)length { - RNSVGLengthUnitType unit = length.unit; - if (unit == SVG_LENGTHTYPE_NUMBER){ - return length.value; - } else if (unit == SVG_LENGTHTYPE_PERCENTAGE){ - return length.value / 100 * [self getCanvasWidth]; - } - return [self fromRelative:length]; + RNSVGLengthUnitType unit = length.unit; + if (unit == SVG_LENGTHTYPE_NUMBER) { + return length.value; + } else if (unit == SVG_LENGTHTYPE_PERCENTAGE) { + return length.value / 100 * [self getCanvasWidth]; + } + return [self fromRelative:length]; } - (CGFloat)relativeOnHeight:(RNSVGLength *)length { - RNSVGLengthUnitType unit = length.unit; - if (unit == SVG_LENGTHTYPE_NUMBER){ - return length.value; - } else if (unit == SVG_LENGTHTYPE_PERCENTAGE){ - return length.value / 100 * [self getCanvasHeight]; - } - return [self fromRelative:length]; + RNSVGLengthUnitType unit = length.unit; + if (unit == SVG_LENGTHTYPE_NUMBER) { + return length.value; + } else if (unit == SVG_LENGTHTYPE_PERCENTAGE) { + return length.value / 100 * [self getCanvasHeight]; + } + return [self fromRelative:length]; } - (CGFloat)relativeOnOther:(RNSVGLength *)length { - RNSVGLengthUnitType unit = length.unit; - if (unit == SVG_LENGTHTYPE_NUMBER){ - return length.value; - } else if (unit == SVG_LENGTHTYPE_PERCENTAGE){ - return length.value / 100 * [self getCanvasDiagonal]; - } - return [self fromRelative:length]; + RNSVGLengthUnitType unit = length.unit; + if (unit == SVG_LENGTHTYPE_NUMBER) { + return length.value; + } else if (unit == SVG_LENGTHTYPE_PERCENTAGE) { + return length.value / 100 * [self getCanvasDiagonal]; + } + return [self fromRelative:length]; } -- (CGFloat)fromRelative:(RNSVGLength*)length { - CGFloat unit; - switch (length.unit) { - case SVG_LENGTHTYPE_EMS: - unit = [self getFontSizeFromContext]; - break; - case SVG_LENGTHTYPE_EXS: - unit = [self getFontSizeFromContext] / 2; - break; +- (CGFloat)fromRelative:(RNSVGLength *)length +{ + CGFloat unit; + switch (length.unit) { + case SVG_LENGTHTYPE_EMS: + unit = [self getFontSizeFromContext]; + break; + case SVG_LENGTHTYPE_EXS: + unit = [self getFontSizeFromContext] / 2; + break; - case SVG_LENGTHTYPE_CM: - unit = (CGFloat)35.43307; - break; - case SVG_LENGTHTYPE_MM: - unit = (CGFloat)3.543307; - break; - case SVG_LENGTHTYPE_IN: - unit = 90; - break; - case SVG_LENGTHTYPE_PT: - unit = 1.25; - break; - case SVG_LENGTHTYPE_PC: - unit = 15; - break; + case SVG_LENGTHTYPE_CM: + unit = (CGFloat)35.43307; + break; + case SVG_LENGTHTYPE_MM: + unit = (CGFloat)3.543307; + break; + case SVG_LENGTHTYPE_IN: + unit = 90; + break; + case SVG_LENGTHTYPE_PT: + unit = 1.25; + break; + case SVG_LENGTHTYPE_PC: + unit = 15; + break; - default: - unit = 1; - } - return length.value * unit; + default: + unit = 1; + } + return length.value * unit; } - (CGRect)getContextBounds { - return CGContextGetClipBoundingBox(UIGraphicsGetCurrentContext()); + return CGContextGetClipBoundingBox(UIGraphicsGetCurrentContext()); } - (CGFloat)getContextWidth { - return CGRectGetWidth([self getContextBounds]); + return CGRectGetWidth([self getContextBounds]); } - (CGFloat)getContextHeight { - return CGRectGetHeight([self getContextBounds]); + return CGRectGetHeight([self getContextBounds]); } -- (CGFloat)getContextDiagonal { - CGRect bounds = [self getContextBounds]; - CGFloat width = CGRectGetWidth(bounds); - CGFloat height = CGRectGetHeight(bounds); - CGFloat powX = width * width; - CGFloat powY = height * height; - CGFloat r = sqrt(powX + powY) * RNSVG_M_SQRT1_2l; - return r; +- (CGFloat)getContextDiagonal +{ + CGRect bounds = [self getContextBounds]; + CGFloat width = CGRectGetWidth(bounds); + CGFloat height = CGRectGetHeight(bounds); + CGFloat powX = width * width; + CGFloat powY = height * height; + CGFloat r = sqrt(powX + powY) * RNSVG_M_SQRT1_2l; + return r; } -- (CGFloat) getCanvasWidth { - if (canvasWidth != -1) { - return canvasWidth; - } - RNSVGGroup* root = [self textRoot]; - if (root == nil) { - canvasWidth = [self getContextWidth]; - } else { - canvasWidth = [[root getGlyphContext] getWidth]; - } - +- (CGFloat)getCanvasWidth +{ + if (canvasWidth != -1) { return canvasWidth; + } + RNSVGGroup *root = [self textRoot]; + if (root == nil) { + canvasWidth = [self getContextWidth]; + } else { + canvasWidth = [[root getGlyphContext] getWidth]; + } + + return canvasWidth; } -- (CGFloat) getCanvasHeight { - if (canvasHeight != -1) { - return canvasHeight; - } - RNSVGGroup* root = [self textRoot]; - if (root == nil) { - canvasHeight = [self getContextHeight]; - } else { - canvasHeight = [[root getGlyphContext] getHeight]; - } - +- (CGFloat)getCanvasHeight +{ + if (canvasHeight != -1) { return canvasHeight; + } + RNSVGGroup *root = [self textRoot]; + if (root == nil) { + canvasHeight = [self getContextHeight]; + } else { + canvasHeight = [[root getGlyphContext] getHeight]; + } + + return canvasHeight; } -- (CGFloat) getCanvasDiagonal { - if (canvasDiagonal != -1) { - return canvasDiagonal; - } - CGFloat width = [self getCanvasWidth]; - CGFloat height = [self getCanvasHeight]; - CGFloat powX = width * width; - CGFloat powY = height * height; - canvasDiagonal = sqrt(powX + powY) * RNSVG_M_SQRT1_2l; +- (CGFloat)getCanvasDiagonal +{ + if (canvasDiagonal != -1) { return canvasDiagonal; + } + CGFloat width = [self getCanvasWidth]; + CGFloat height = [self getCanvasHeight]; + CGFloat powX = width * width; + CGFloat powY = height * height; + canvasDiagonal = sqrt(powX + powY) * RNSVG_M_SQRT1_2l; + return canvasDiagonal; } - (void)parseReference { - self.dirty = false; - if (self.name) { - __typeof__(self) __weak weakSelf = self; - [self.svgView defineTemplate:weakSelf templateName:self.name]; - } + self.dirty = false; + if (self.name) { + __typeof__(self) __weak weakSelf = self; + [self.svgView defineTemplate:weakSelf templateName:self.name]; + } } - (void)traverseSubviews:(BOOL (^)(__kindof RNSVGView *node))block { - for (RNSVGView *node in self.subviews) { - if (!block(node)) { - break; - } + for (RNSVGView *node in self.subviews) { + if (!block(node)) { + break; } + } } - (void)dealloc { - CGPathRelease(_cachedClipPath); - CGPathRelease(_strokePath); - CGPathRelease(_path); + CGPathRelease(_cachedClipPath); + CGPathRelease(_strokePath); + CGPathRelease(_path); } #ifdef RN_FABRIC_ENABLED - (void)prepareForRecycle { - [super prepareForRecycle]; - - self.opacity = 1; - self.opaque = false; - self.matrix = CGAffineTransformIdentity; - self.transforms = CGAffineTransformIdentity; - self.invTransform = CGAffineTransformIdentity; - _merging = false; - _dirty = false; - - _name = nil; - _display = nil; - _opacity = 0; - _clipRule = kRNSVGCGFCRuleEvenodd; - _clipPath = nil; - _mask = nil; - _markerStart = nil; - _markerMid = nil; - _markerEnd = nil; - _parentComponentView = nil; + [super prepareForRecycle]; - _pointerEvents = RCTPointerEventsUnspecified; - _responsible = NO; + self.opacity = 1; + self.opaque = false; + self.matrix = CGAffineTransformIdentity; + self.transforms = CGAffineTransformIdentity; + self.invTransform = CGAffineTransformIdentity; + _merging = false; + _dirty = false; - _ctm = CGAffineTransformIdentity; - _screenCTM = CGAffineTransformIdentity; - _matrix = CGAffineTransformIdentity; - _transforms = CGAffineTransformIdentity; - _invmatrix = CGAffineTransformIdentity; - _invTransform = CGAffineTransformIdentity; - _active = NO; - _skip = NO; - if (_markerPath) { - CGPathRelease(_markerPath); - } - _markerPath = nil; - _clientRect = CGRectZero; - _pathBounds = CGRectZero; - _fillBounds = CGRectZero; - _strokeBounds = CGRectZero; - _markerBounds = CGRectZero; - _onLayout = nil; + _name = nil; + _display = nil; + _opacity = 0; + _clipRule = kRNSVGCGFCRuleEvenodd; + _clipPath = nil; + _mask = nil; + _markerStart = nil; + _markerMid = nil; + _markerEnd = nil; + _parentComponentView = nil; - _svgView = nil; - _textRoot = nil; - - - glyphContext = nil; - _transparent = NO; - _clipNode = nil; - canvasWidth = 0; - canvasHeight = 0; - canvasDiagonal = 0; - CGPathRelease(_cachedClipPath); - _cachedClipPath = nil; - CGPathRelease(_strokePath); - _strokePath = nil; - CGPathRelease(_path); - _path = nil; + _pointerEvents = RCTPointerEventsUnspecified; + _responsible = NO; + + _ctm = CGAffineTransformIdentity; + _screenCTM = CGAffineTransformIdentity; + _matrix = CGAffineTransformIdentity; + _transforms = CGAffineTransformIdentity; + _invmatrix = CGAffineTransformIdentity; + _invTransform = CGAffineTransformIdentity; + _active = NO; + _skip = NO; + if (_markerPath) { + CGPathRelease(_markerPath); + } + _markerPath = nil; + _clientRect = CGRectZero; + _pathBounds = CGRectZero; + _fillBounds = CGRectZero; + _strokeBounds = CGRectZero; + _markerBounds = CGRectZero; + _onLayout = nil; + + _svgView = nil; + _textRoot = nil; + + glyphContext = nil; + _transparent = NO; + _clipNode = nil; + canvasWidth = 0; + canvasHeight = 0; + canvasDiagonal = 0; + CGPathRelease(_cachedClipPath); + _cachedClipPath = nil; + CGPathRelease(_strokePath); + _strokePath = nil; + CGPathRelease(_path); + _path = nil; } #endif // RN_FABRIC_ENABLED diff --git a/apple/RNSVGRenderable.h b/apple/RNSVGRenderable.h index 6227a575..a4f02baa 100644 --- a/apple/RNSVGRenderable.h +++ b/apple/RNSVGRenderable.h @@ -12,8 +12,8 @@ #import "RNSVGBrush.h" #import "RNSVGCGFCRule.h" -#import "RNSVGNode.h" #import "RNSVGLength.h" +#import "RNSVGNode.h" #import "RNSVGVectorEffect.h" @interface RNSVGRenderable : RNSVGNode diff --git a/apple/RNSVGRenderable.mm b/apple/RNSVGRenderable.mm index a2bd72b8..4ac4fe14 100644 --- a/apple/RNSVGRenderable.mm +++ b/apple/RNSVGRenderable.mm @@ -6,650 +6,660 @@ * LICENSE file in the root directory of this source tree. */ -#import #import "RNSVGRenderable.h" -#import "RNSVGClipPath.h" -#import "RNSVGMask.h" -#import "RNSVGViewBox.h" -#import "RNSVGVectorEffect.h" +#import #import "RNSVGBezierElement.h" +#import "RNSVGClipPath.h" #import "RNSVGMarker.h" #import "RNSVGMarkerPosition.h" +#import "RNSVGMask.h" +#import "RNSVGVectorEffect.h" +#import "RNSVGViewBox.h" -@implementation RNSVGRenderable -{ - NSMutableDictionary *_originProperties; - NSArray *_lastMergedList; - NSArray *_attributeList; - NSArray *_sourceStrokeDashArray; - CGFloat *_strokeDashArrayData; - CGPathRef _srcHitPath; +@implementation RNSVGRenderable { + NSMutableDictionary *_originProperties; + NSArray *_lastMergedList; + NSArray *_attributeList; + NSArray *_sourceStrokeDashArray; + CGFloat *_strokeDashArrayData; + CGPathRef _srcHitPath; } -static RNSVGRenderable * _contextElement; -+ (RNSVGRenderable *)contextElement { return _contextElement; } -+ (void)setContextElement:(RNSVGRenderable *)contextElement { _contextElement = contextElement; } +static RNSVGRenderable *_contextElement; ++ (RNSVGRenderable *)contextElement +{ + return _contextElement; +} ++ (void)setContextElement:(RNSVGRenderable *)contextElement +{ + _contextElement = contextElement; +} - (id)init { - if (self = [super init]) { - _fillOpacity = 1; - _strokeOpacity = 1; - _strokeWidth = [RNSVGLength lengthWithNumber:1]; - _fillRule = kRNSVGCGFCRuleNonzero; - } - return self; + if (self = [super init]) { + _fillOpacity = 1; + _strokeOpacity = 1; + _strokeWidth = [RNSVGLength lengthWithNumber:1]; + _fillRule = kRNSVGCGFCRuleNonzero; + } + return self; } - (void)invalidate { - _sourceStrokeDashArray = nil; - if (self.dirty || self.merging) { - return; - } - _srcHitPath = nil; - [super invalidate]; - self.dirty = true; + _sourceStrokeDashArray = nil; + if (self.dirty || self.merging) { + return; + } + _srcHitPath = nil; + [super invalidate]; + self.dirty = true; } - (void)setFill:(RNSVGBrush *)fill { - if (fill == _fill) { - return; - } - [self invalidate]; - _fill = fill; + if (fill == _fill) { + return; + } + [self invalidate]; + _fill = fill; } - (void)setFillOpacity:(CGFloat)fillOpacity { - if (fillOpacity == _fillOpacity) { - return; - } - [self invalidate]; - _fillOpacity = fillOpacity; + if (fillOpacity == _fillOpacity) { + return; + } + [self invalidate]; + _fillOpacity = fillOpacity; } - (void)setFillRule:(RNSVGCGFCRule)fillRule { - if (fillRule == _fillRule) { - return; - } - [self invalidate]; - _fillRule = fillRule; + if (fillRule == _fillRule) { + return; + } + [self invalidate]; + _fillRule = fillRule; } - (void)setStroke:(RNSVGBrush *)stroke { - if (stroke == _stroke) { - return; - } - [self invalidate]; - _stroke = stroke; + if (stroke == _stroke) { + return; + } + [self invalidate]; + _stroke = stroke; } - (void)setStrokeOpacity:(CGFloat)strokeOpacity { - if (strokeOpacity == _strokeOpacity) { - return; - } - [self invalidate]; - _strokeOpacity = strokeOpacity; + if (strokeOpacity == _strokeOpacity) { + return; + } + [self invalidate]; + _strokeOpacity = strokeOpacity; } -- (void)setStrokeWidth:(RNSVGLength*)strokeWidth +- (void)setStrokeWidth:(RNSVGLength *)strokeWidth { - if ([strokeWidth isEqualTo:_strokeWidth]) { - return; - } - [self invalidate]; - _strokeWidth = strokeWidth; + if ([strokeWidth isEqualTo:_strokeWidth]) { + return; + } + [self invalidate]; + _strokeWidth = strokeWidth; } - (void)setStrokeLinecap:(CGLineCap)strokeLinecap { - if (strokeLinecap == _strokeLinecap) { - return; - } - [self invalidate]; - _strokeLinecap = strokeLinecap; + if (strokeLinecap == _strokeLinecap) { + return; + } + [self invalidate]; + _strokeLinecap = strokeLinecap; } - (void)setStrokeJoin:(CGLineJoin)strokeLinejoin { - if (strokeLinejoin == _strokeLinejoin) { - return; - } - [self invalidate]; - _strokeLinejoin = strokeLinejoin; + if (strokeLinejoin == _strokeLinejoin) { + return; + } + [self invalidate]; + _strokeLinejoin = strokeLinejoin; } - (void)setStrokeMiterlimit:(CGFloat)strokeMiterlimit { - if (strokeMiterlimit == _strokeMiterlimit) { - return; - } - [self invalidate]; - _strokeMiterlimit = strokeMiterlimit; + if (strokeMiterlimit == _strokeMiterlimit) { + return; + } + [self invalidate]; + _strokeMiterlimit = strokeMiterlimit; } - (void)setStrokeDasharray:(NSArray *)strokeDasharray { - if (strokeDasharray == _strokeDasharray) { - return; - } - [self invalidate]; - _strokeDasharray = strokeDasharray; + if (strokeDasharray == _strokeDasharray) { + return; + } + [self invalidate]; + _strokeDasharray = strokeDasharray; } - (void)setStrokeDashoffset:(CGFloat)strokeDashoffset { - if (strokeDashoffset == _strokeDashoffset) { - return; - } - [self invalidate]; - _strokeDashoffset = strokeDashoffset; + if (strokeDashoffset == _strokeDashoffset) { + return; + } + [self invalidate]; + _strokeDashoffset = strokeDashoffset; } - (void)setVectorEffect:(RNSVGVectorEffect)vectorEffect { - if (vectorEffect == _vectorEffect) { - return; - } - [self invalidate]; - _vectorEffect = vectorEffect; + if (vectorEffect == _vectorEffect) { + return; + } + [self invalidate]; + _vectorEffect = vectorEffect; } - (void)setPropList:(NSArray *)propList { - if (propList == _propList) { - return; - } + if (propList == _propList) { + return; + } - _propList = _attributeList = propList; - [self invalidate]; + _propList = _attributeList = propList; + [self invalidate]; } - (void)dealloc { - CGPathRelease(_hitArea); - _sourceStrokeDashArray = nil; - if (_strokeDashArrayData) { - free(_strokeDashArrayData); - } - _strokeDashArrayData = nil; + CGPathRelease(_hitArea); + _sourceStrokeDashArray = nil; + if (_strokeDashArrayData) { + free(_strokeDashArrayData); + } + _strokeDashArrayData = nil; } #ifdef RN_FABRIC_ENABLED - (void)prepareForRecycle { - [super prepareForRecycle]; + [super prepareForRecycle]; - _fillOpacity = 1; - _strokeOpacity = 1; - _strokeWidth = [RNSVGLength lengthWithNumber:1]; - _fillRule = kRNSVGCGFCRuleNonzero; - - _originProperties = nil; - _lastMergedList = nil; - _attributeList = nil; - _sourceStrokeDashArray = nil; - _strokeDashArrayData = nil; - _srcHitPath = nil; - CGPathRelease(_hitArea); - _hitArea = nil; - _sourceStrokeDashArray = nil; - if (_strokeDashArrayData) { - free(_strokeDashArrayData); - } - _strokeDashArrayData = nil; - - _contextElement = nil; - _fill = nil; - _stroke = nil; - _strokeLinecap = kCGLineCapButt; - _strokeLinejoin = kCGLineJoinMiter; - _strokeMiterlimit = 0; - _strokeDasharray = nil; - _strokeDashoffset = 0; - _vectorEffect = kRNSVGVectorEffectDefault; - _propList = nil; + _fillOpacity = 1; + _strokeOpacity = 1; + _strokeWidth = [RNSVGLength lengthWithNumber:1]; + _fillRule = kRNSVGCGFCRuleNonzero; + + _originProperties = nil; + _lastMergedList = nil; + _attributeList = nil; + _sourceStrokeDashArray = nil; + _strokeDashArrayData = nil; + _srcHitPath = nil; + CGPathRelease(_hitArea); + _hitArea = nil; + _sourceStrokeDashArray = nil; + if (_strokeDashArrayData) { + free(_strokeDashArrayData); + } + _strokeDashArrayData = nil; + + _contextElement = nil; + _fill = nil; + _stroke = nil; + _strokeLinecap = kCGLineCapButt; + _strokeLinejoin = kCGLineJoinMiter; + _strokeMiterlimit = 0; + _strokeDasharray = nil; + _strokeDashoffset = 0; + _vectorEffect = kRNSVGVectorEffectDefault; + _propList = nil; } #endif // RN_FABRIC_ENABLED -UInt32 saturate(CGFloat value) { - return value <= 0 ? 0 : value >= 255 ? 255 : (UInt32)value; +UInt32 saturate(CGFloat value) +{ + return value <= 0 ? 0 : value >= 255 ? 255 : (UInt32)value; } - (void)renderTo:(CGContextRef)context rect:(CGRect)rect { - self.dirty = false; - // This needs to be painted on a layer before being composited. - CGContextSaveGState(context); - CGContextConcatCTM(context, self.matrix); - CGContextConcatCTM(context, self.transforms); - CGContextSetAlpha(context, self.opacity); + self.dirty = false; + // This needs to be painted on a layer before being composited. + CGContextSaveGState(context); + CGContextConcatCTM(context, self.matrix); + CGContextConcatCTM(context, self.transforms); + CGContextSetAlpha(context, self.opacity); - [self beginTransparencyLayer:context]; + [self beginTransparencyLayer:context]; - if (self.mask) { - // https://www.w3.org/TR/SVG11/masking.html#MaskElement - RNSVGMask *_maskNode = (RNSVGMask*)[self.svgView getDefinedMask:self.mask]; - CGRect bounds = CGContextGetClipBoundingBox(context); - CGSize boundsSize = bounds.size; - CGFloat height = boundsSize.height; - CGFloat width = boundsSize.width; + if (self.mask) { + // https://www.w3.org/TR/SVG11/masking.html#MaskElement + RNSVGMask *_maskNode = (RNSVGMask *)[self.svgView getDefinedMask:self.mask]; + CGRect bounds = CGContextGetClipBoundingBox(context); + CGSize boundsSize = bounds.size; + CGFloat height = boundsSize.height; + CGFloat width = boundsSize.width; #if TARGET_OS_OSX - CGFloat scale = [[NSScreen mainScreen] backingScaleFactor]; + CGFloat scale = [[NSScreen mainScreen] backingScaleFactor]; #else - CGFloat scale = [[UIScreen mainScreen] scale]; + CGFloat scale = [[UIScreen mainScreen] scale]; #endif // TARGET_OS_OSX - NSUInteger iheight = (NSUInteger)height; - NSUInteger iwidth = (NSUInteger)width; - NSUInteger iscale = (NSUInteger)scale; - NSUInteger scaledHeight = iheight * iscale; - NSUInteger scaledWidth = iwidth * iscale; - NSUInteger npixels = scaledHeight * scaledWidth; - CGRect drawBounds = CGRectMake(0, 0, width, height); + NSUInteger iheight = (NSUInteger)height; + NSUInteger iwidth = (NSUInteger)width; + NSUInteger iscale = (NSUInteger)scale; + NSUInteger scaledHeight = iheight * iscale; + NSUInteger scaledWidth = iwidth * iscale; + NSUInteger npixels = scaledHeight * scaledWidth; + CGRect drawBounds = CGRectMake(0, 0, width, height); - // Allocate pixel buffer and bitmap context for mask - NSUInteger bytesPerPixel = 4; - NSUInteger bitsPerComponent = 8; - NSUInteger bytesPerRow = bytesPerPixel * scaledWidth; - CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); - UInt32 * pixels = (UInt32 *) calloc(npixels, sizeof(UInt32)); - CGContextRef bcontext = CGBitmapContextCreate(pixels, scaledWidth, scaledHeight, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big); - CGContextScaleCTM(bcontext, iscale, iscale); + // Allocate pixel buffer and bitmap context for mask + NSUInteger bytesPerPixel = 4; + NSUInteger bitsPerComponent = 8; + NSUInteger bytesPerRow = bytesPerPixel * scaledWidth; + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + UInt32 *pixels = (UInt32 *)calloc(npixels, sizeof(UInt32)); + CGContextRef bcontext = CGBitmapContextCreate( + pixels, + scaledWidth, + scaledHeight, + bitsPerComponent, + bytesPerRow, + colorSpace, + kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big); + CGContextScaleCTM(bcontext, iscale, iscale); - // Clip to mask bounds and render the mask - CGFloat x = [self relativeOn:[_maskNode x] - relative:width]; - CGFloat y = [self relativeOn:[_maskNode y] - relative:height]; - CGFloat w = [self relativeOn:[_maskNode maskwidth] - relative:width]; - CGFloat h = [self relativeOn:[_maskNode maskheight] - relative:height]; - CGRect maskBounds = CGRectMake(x, y, w, h); - CGContextClipToRect(bcontext, maskBounds); - [_maskNode renderLayerTo:bcontext rect:rect]; + // Clip to mask bounds and render the mask + CGFloat x = [self relativeOn:[_maskNode x] relative:width]; + CGFloat y = [self relativeOn:[_maskNode y] relative:height]; + CGFloat w = [self relativeOn:[_maskNode maskwidth] relative:width]; + CGFloat h = [self relativeOn:[_maskNode maskheight] relative:height]; + CGRect maskBounds = CGRectMake(x, y, w, h); + CGContextClipToRect(bcontext, maskBounds); + [_maskNode renderLayerTo:bcontext rect:rect]; - // Apply luminanceToAlpha filter primitive - // https://www.w3.org/TR/SVG11/filters.html#feColorMatrixElement - UInt32 * currentPixel = pixels; - for (NSUInteger i = 0; i < npixels; i++) { - UInt32 color = *currentPixel; + // Apply luminanceToAlpha filter primitive + // https://www.w3.org/TR/SVG11/filters.html#feColorMatrixElement + UInt32 *currentPixel = pixels; + for (NSUInteger i = 0; i < npixels; i++) { + UInt32 color = *currentPixel; - UInt32 r = color & 0xFF; - UInt32 g = (color >> 8) & 0xFF; - UInt32 b = (color >> 16) & 0xFF; + UInt32 r = color & 0xFF; + UInt32 g = (color >> 8) & 0xFF; + UInt32 b = (color >> 16) & 0xFF; - CGFloat luma = (CGFloat)(0.299 * r + 0.587 * g + 0.144 * b); - *currentPixel = saturate(luma) << 24; - currentPixel++; - } - - // Create mask image and release memory - CGImageRef maskImage = CGBitmapContextCreateImage(bcontext); - CGColorSpaceRelease(colorSpace); - CGContextRelease(bcontext); - free(pixels); - - // Render content of current SVG Renderable to image - UIGraphicsBeginImageContextWithOptions(boundsSize, NO, 0.0); - CGContextRef newContext = UIGraphicsGetCurrentContext(); - CGContextTranslateCTM(newContext, 0.0, height); - CGContextScaleCTM(newContext, 1.0, -1.0); - [self renderLayerTo:newContext rect:rect]; - CGImageRef contentImage = CGBitmapContextCreateImage(newContext); - UIGraphicsEndImageContext(); - - // Blend current element and mask - UIGraphicsBeginImageContextWithOptions(boundsSize, NO, 0.0); - newContext = UIGraphicsGetCurrentContext(); - CGContextTranslateCTM(newContext, 0.0, height); - CGContextScaleCTM(newContext, 1.0, -1.0); - - CGContextSetBlendMode(newContext, kCGBlendModeCopy); - CGContextDrawImage(newContext, drawBounds, maskImage); - CGImageRelease(maskImage); - - CGContextSetBlendMode(newContext, kCGBlendModeSourceIn); - CGContextDrawImage(newContext, drawBounds, contentImage); - CGImageRelease(contentImage); - - CGImageRef blendedImage = CGBitmapContextCreateImage(newContext); - UIGraphicsEndImageContext(); - - // Render blended result into current render context - CGContextDrawImage(context, drawBounds, blendedImage); - CGImageRelease(blendedImage); - } else { - [self renderLayerTo:context rect:rect]; + CGFloat luma = (CGFloat)(0.299 * r + 0.587 * g + 0.144 * b); + *currentPixel = saturate(luma) << 24; + currentPixel++; } - [self endTransparencyLayer:context]; - CGContextRestoreGState(context); + // Create mask image and release memory + CGImageRef maskImage = CGBitmapContextCreateImage(bcontext); + CGColorSpaceRelease(colorSpace); + CGContextRelease(bcontext); + free(pixels); - [self renderMarkers:context path:self.path rect:&rect]; + // Render content of current SVG Renderable to image + UIGraphicsBeginImageContextWithOptions(boundsSize, NO, 0.0); + CGContextRef newContext = UIGraphicsGetCurrentContext(); + CGContextTranslateCTM(newContext, 0.0, height); + CGContextScaleCTM(newContext, 1.0, -1.0); + [self renderLayerTo:newContext rect:rect]; + CGImageRef contentImage = CGBitmapContextCreateImage(newContext); + UIGraphicsEndImageContext(); + + // Blend current element and mask + UIGraphicsBeginImageContextWithOptions(boundsSize, NO, 0.0); + newContext = UIGraphicsGetCurrentContext(); + CGContextTranslateCTM(newContext, 0.0, height); + CGContextScaleCTM(newContext, 1.0, -1.0); + + CGContextSetBlendMode(newContext, kCGBlendModeCopy); + CGContextDrawImage(newContext, drawBounds, maskImage); + CGImageRelease(maskImage); + + CGContextSetBlendMode(newContext, kCGBlendModeSourceIn); + CGContextDrawImage(newContext, drawBounds, contentImage); + CGImageRelease(contentImage); + + CGImageRef blendedImage = CGBitmapContextCreateImage(newContext); + UIGraphicsEndImageContext(); + + // Render blended result into current render context + CGContextDrawImage(context, drawBounds, blendedImage); + CGImageRelease(blendedImage); + } else { + [self renderLayerTo:context rect:rect]; + } + [self endTransparencyLayer:context]; + + CGContextRestoreGState(context); + + [self renderMarkers:context path:self.path rect:&rect]; } -- (void)prepareStrokeDash:(NSUInteger)count strokeDasharray:(NSArray *)strokeDasharray { - if (strokeDasharray != _sourceStrokeDashArray) { - CGFloat *dash = _strokeDashArrayData; - _strokeDashArrayData = (CGFloat *)realloc(dash, sizeof(CGFloat) * count); - if (!_strokeDashArrayData) { - free(dash); - return; - } - _sourceStrokeDashArray = strokeDasharray; - for (NSUInteger i = 0; i < count; i++) { - _strokeDashArrayData[i] = (CGFloat)[self relativeOnOther:strokeDasharray[i]]; - } +- (void)prepareStrokeDash:(NSUInteger)count strokeDasharray:(NSArray *)strokeDasharray +{ + if (strokeDasharray != _sourceStrokeDashArray) { + CGFloat *dash = _strokeDashArrayData; + _strokeDashArrayData = (CGFloat *)realloc(dash, sizeof(CGFloat) * count); + if (!_strokeDashArrayData) { + free(dash); + return; } + _sourceStrokeDashArray = strokeDasharray; + for (NSUInteger i = 0; i < count; i++) { + _strokeDashArrayData[i] = (CGFloat)[self relativeOnOther:strokeDasharray[i]]; + } + } } -- (void)renderMarkers:(CGContextRef)context path:(CGPathRef)path rect:(const CGRect *)rect { - RNSVGMarker *markerStart = (RNSVGMarker*)[self.svgView getDefinedMarker:self.markerStart]; - RNSVGMarker *markerMid = (RNSVGMarker*)[self.svgView getDefinedMarker:self.markerMid]; - RNSVGMarker *markerEnd = (RNSVGMarker*)[self.svgView getDefinedMarker:self.markerEnd]; - if (markerStart || markerMid || markerEnd) { - _contextElement = self; - NSArray* positions = [RNSVGMarkerPosition fromCGPath:path]; - CGFloat width = self.strokeWidth ? [self relativeOnOther:self.strokeWidth] : 1; - __block CGRect bounds = CGRectNull; - CGMutablePathRef markerPath = CGPathCreateMutable(); - for (RNSVGMarkerPosition* position in positions) { - RNSVGMarkerType type = [position type]; - RNSVGMarker *marker; - switch (type) { - case kStartMarker: - marker = markerStart; - break; +- (void)renderMarkers:(CGContextRef)context path:(CGPathRef)path rect:(const CGRect *)rect +{ + RNSVGMarker *markerStart = (RNSVGMarker *)[self.svgView getDefinedMarker:self.markerStart]; + RNSVGMarker *markerMid = (RNSVGMarker *)[self.svgView getDefinedMarker:self.markerMid]; + RNSVGMarker *markerEnd = (RNSVGMarker *)[self.svgView getDefinedMarker:self.markerEnd]; + if (markerStart || markerMid || markerEnd) { + _contextElement = self; + NSArray *positions = [RNSVGMarkerPosition fromCGPath:path]; + CGFloat width = self.strokeWidth ? [self relativeOnOther:self.strokeWidth] : 1; + __block CGRect bounds = CGRectNull; + CGMutablePathRef markerPath = CGPathCreateMutable(); + for (RNSVGMarkerPosition *position in positions) { + RNSVGMarkerType type = [position type]; + RNSVGMarker *marker; + switch (type) { + case kStartMarker: + marker = markerStart; + break; - case kMidMarker: - marker = markerMid; - break; + case kMidMarker: + marker = markerMid; + break; - case kEndMarker: - marker = markerEnd; - break; - } - if (!marker) { - continue; - } + case kEndMarker: + marker = markerEnd; + break; + } + if (!marker) { + continue; + } - [marker renderMarker:context rect:*rect position:position strokeWidth:width]; - CGAffineTransform transform = marker.transform; - CGPathRef hitArea = marker.hitArea; - CGPathAddPath(markerPath, &transform, hitArea); - CGRect nodeRect = marker.pathBounds; - if (!CGRectIsEmpty(nodeRect)) { - bounds = CGRectUnion(bounds, CGRectApplyAffineTransform(nodeRect, transform)); - } - } - self.markerBounds = bounds; - self.markerPath = markerPath; - _contextElement = nil; + [marker renderMarker:context rect:*rect position:position strokeWidth:width]; + CGAffineTransform transform = marker.transform; + CGPathRef hitArea = marker.hitArea; + CGPathAddPath(markerPath, &transform, hitArea); + CGRect nodeRect = marker.pathBounds; + if (!CGRectIsEmpty(nodeRect)) { + bounds = CGRectUnion(bounds, CGRectApplyAffineTransform(nodeRect, transform)); + } } + self.markerBounds = bounds; + self.markerPath = markerPath; + _contextElement = nil; + } } - (void)renderLayerTo:(CGContextRef)context rect:(CGRect)rect { - CGPathRef path = self.path; - if (!path) { - path = [self getPath:context]; - if (!self.path) { - self.path = CGPathRetain(path); - } - [self setHitArea:path]; - self.fillBounds = CGPathGetBoundingBox(path); - self.strokeBounds = CGPathGetBoundingBox(self.strokePath); - self.pathBounds = CGRectUnion(self.fillBounds, self.strokeBounds); + CGPathRef path = self.path; + if (!path) { + path = [self getPath:context]; + if (!self.path) { + self.path = CGPathRetain(path); } - const CGRect pathBounds = self.pathBounds; + [self setHitArea:path]; + self.fillBounds = CGPathGetBoundingBox(path); + self.strokeBounds = CGPathGetBoundingBox(self.strokePath); + self.pathBounds = CGRectUnion(self.fillBounds, self.strokeBounds); + } + const CGRect pathBounds = self.pathBounds; - CGAffineTransform current = CGContextGetCTM(context); - CGAffineTransform svgToClientTransform = CGAffineTransformConcat(current, self.svgView.invInitialCTM); - CGRect clientRect = CGRectApplyAffineTransform(pathBounds, svgToClientTransform); + CGAffineTransform current = CGContextGetCTM(context); + CGAffineTransform svgToClientTransform = CGAffineTransformConcat(current, self.svgView.invInitialCTM); + CGRect clientRect = CGRectApplyAffineTransform(pathBounds, svgToClientTransform); - self.ctm = svgToClientTransform; - self.clientRect = clientRect; - self.screenCTM = current; + self.ctm = svgToClientTransform; + self.clientRect = clientRect; + self.screenCTM = current; - if (_vectorEffect == kRNSVGVectorEffectNonScalingStroke) { - path = CGPathCreateCopyByTransformingPath(path, &svgToClientTransform); - CGContextConcatCTM(context, CGAffineTransformInvert(svgToClientTransform)); + if (_vectorEffect == kRNSVGVectorEffectNonScalingStroke) { + path = CGPathCreateCopyByTransformingPath(path, &svgToClientTransform); + CGContextConcatCTM(context, CGAffineTransformInvert(svgToClientTransform)); + } + + CGAffineTransform vbmatrix = self.svgView.getViewBoxTransform; + CGAffineTransform transform = CGAffineTransformConcat(self.matrix, self.transforms); + CGAffineTransform matrix = CGAffineTransformConcat(transform, vbmatrix); + + CGRect bounds = CGRectMake(0, 0, CGRectGetWidth(clientRect), CGRectGetHeight(clientRect)); + CGPoint mid = CGPointMake(CGRectGetMidX(pathBounds), CGRectGetMidY(pathBounds)); + CGPoint center = CGPointApplyAffineTransform(mid, matrix); + + self.bounds = bounds; + if (!isnan(center.x) && !isnan(center.y)) { + self.center = center; + } + self.frame = clientRect; + + if (self.skip || self.opacity == 0) { + return; + } + + if (!self.fill && !self.stroke) { + return; + } + + CGPathDrawingMode mode = kCGPathStroke; + BOOL fillColor = NO; + [self clip:context]; + + BOOL evenodd = self.fillRule == kRNSVGCGFCRuleEvenodd; + + if (self.fill) { + if (self.fill.class == RNSVGBrush.class) { + CGContextSetFillColorWithColor(context, [self.tintColor CGColor]); + fillColor = YES; + } else { + fillColor = [self.fill applyFillColor:context opacity:self.fillOpacity]; } - CGAffineTransform vbmatrix = self.svgView.getViewBoxTransform; - CGAffineTransform transform = CGAffineTransformConcat(self.matrix, self.transforms); - CGAffineTransform matrix = CGAffineTransformConcat(transform, vbmatrix); + if (fillColor) { + mode = evenodd ? kCGPathEOFill : kCGPathFill; + } else { + CGContextSaveGState(context); + CGContextAddPath(context, path); + evenodd ? CGContextEOClip(context) : CGContextClip(context); + [self.fill paint:context + opacity:self.fillOpacity + painter:[self.svgView getDefinedPainter:self.fill.brushRef] + bounds:pathBounds]; + CGContextRestoreGState(context); - CGRect bounds = CGRectMake(0, 0, CGRectGetWidth(clientRect), CGRectGetHeight(clientRect)); - CGPoint mid = CGPointMake(CGRectGetMidX(pathBounds), CGRectGetMidY(pathBounds)); - CGPoint center = CGPointApplyAffineTransform(mid, matrix); - - self.bounds = bounds; - if (!isnan(center.x) && !isnan(center.y)) { - self.center = center; - } - self.frame = clientRect; - - if (self.skip || self.opacity == 0) { + if (!self.stroke) { return; + } + } + } + + if (self.stroke) { + CGFloat width = self.strokeWidth ? [self relativeOnOther:self.strokeWidth] : 1; + CGContextSetLineWidth(context, width); + CGContextSetLineCap(context, self.strokeLinecap); + CGContextSetLineJoin(context, self.strokeLinejoin); + NSArray *strokeDasharray = self.strokeDasharray; + NSUInteger count = strokeDasharray.count; + + if (count) { + [self prepareStrokeDash:count strokeDasharray:strokeDasharray]; + if (_strokeDashArrayData) { + CGContextSetLineDash(context, self.strokeDashoffset, _strokeDashArrayData, count); + } } - if (!self.fill && !self.stroke) { - return; + if (!fillColor) { + CGContextAddPath(context, path); + CGContextReplacePathWithStrokedPath(context); + CGContextClip(context); } - CGPathDrawingMode mode = kCGPathStroke; - BOOL fillColor = NO; - [self clip:context]; + BOOL strokeColor; - BOOL evenodd = self.fillRule == kRNSVGCGFCRuleEvenodd; - - if (self.fill) { - if (self.fill.class == RNSVGBrush.class) { - CGContextSetFillColorWithColor(context, [self.tintColor CGColor]); - fillColor = YES; - } else { - fillColor = [self.fill applyFillColor:context opacity:self.fillOpacity]; - } - - if (fillColor) { - mode = evenodd ? kCGPathEOFill : kCGPathFill; - } else { - CGContextSaveGState(context); - CGContextAddPath(context, path); - evenodd ? CGContextEOClip(context) : CGContextClip(context); - [self.fill paint:context - opacity:self.fillOpacity - painter:[self.svgView getDefinedPainter:self.fill.brushRef] - bounds:pathBounds - ]; - CGContextRestoreGState(context); - - if (!self.stroke) { - return; - } - } + if (self.stroke.class == RNSVGBrush.class) { + CGContextSetStrokeColorWithColor(context, [self.tintColor CGColor]); + strokeColor = YES; + } else { + strokeColor = [self.stroke applyStrokeColor:context opacity:self.strokeOpacity]; } - if (self.stroke) { - CGFloat width = self.strokeWidth ? [self relativeOnOther:self.strokeWidth] : 1; - CGContextSetLineWidth(context, width); - CGContextSetLineCap(context, self.strokeLinecap); - CGContextSetLineJoin(context, self.strokeLinejoin); - NSArray* strokeDasharray = self.strokeDasharray; - NSUInteger count = strokeDasharray.count; + if (strokeColor && fillColor) { + mode = evenodd ? kCGPathEOFillStroke : kCGPathFillStroke; + } else if (!strokeColor) { + // draw fill + if (fillColor) { + CGContextAddPath(context, path); + CGContextDrawPath(context, mode); + } - if (count) { - [self prepareStrokeDash:count strokeDasharray:strokeDasharray]; - if (_strokeDashArrayData) { - CGContextSetLineDash(context, self.strokeDashoffset, _strokeDashArrayData, count); - } - } + // draw stroke + CGContextAddPath(context, path); + CGContextReplacePathWithStrokedPath(context); + evenodd ? CGContextEOClip(context) : CGContextClip(context); - if (!fillColor) { - CGContextAddPath(context, path); - CGContextReplacePathWithStrokedPath(context); - CGContextClip(context); - } - - BOOL strokeColor; - - if (self.stroke.class == RNSVGBrush.class) { - CGContextSetStrokeColorWithColor(context,[self.tintColor CGColor]); - strokeColor = YES; - } else { - strokeColor = [self.stroke applyStrokeColor:context opacity:self.strokeOpacity]; - } - - if (strokeColor && fillColor) { - mode = evenodd ? kCGPathEOFillStroke : kCGPathFillStroke; - } else if (!strokeColor) { - // draw fill - if (fillColor) { - CGContextAddPath(context, path); - CGContextDrawPath(context, mode); - } - - // draw stroke - CGContextAddPath(context, path); - CGContextReplacePathWithStrokedPath(context); - evenodd ? CGContextEOClip(context) : CGContextClip(context); - - [self.stroke paint:context - opacity:self.strokeOpacity - painter:[self.svgView getDefinedPainter:self.stroke.brushRef] - bounds:pathBounds - ]; - return; - } + [self.stroke paint:context + opacity:self.strokeOpacity + painter:[self.svgView getDefinedPainter:self.stroke.brushRef] + bounds:pathBounds]; + return; } + } - CGContextAddPath(context, path); - CGContextDrawPath(context, mode); + CGContextAddPath(context, path); + CGContextDrawPath(context, mode); } - (void)setHitArea:(CGPathRef)path { - if (_srcHitPath == path) { - return; - } - _srcHitPath = path; - CGPathRelease(_hitArea); - CGPathRelease(self.strokePath); - _hitArea = CGPathCreateCopy(path); - self.strokePath = nil; - if (self.stroke && self.strokeWidth) { - // Add stroke to hitArea - CGFloat width = [self relativeOnOther:self.strokeWidth]; - self.strokePath = CGPathRetain((CGPathRef)CFAutorelease(CGPathCreateCopyByStrokingPath(path, nil, width, self.strokeLinecap, self.strokeLinejoin, self.strokeMiterlimit))); - // TODO add dashing - // CGPathCreateCopyByDashingPath(CGPathRef _Nullable path, const CGAffineTransform * _Nullable transform, CGFloat phase, const CGFloat * _Nullable lengths, size_t count) - } + if (_srcHitPath == path) { + return; + } + _srcHitPath = path; + CGPathRelease(_hitArea); + CGPathRelease(self.strokePath); + _hitArea = CGPathCreateCopy(path); + self.strokePath = nil; + if (self.stroke && self.strokeWidth) { + // Add stroke to hitArea + CGFloat width = [self relativeOnOther:self.strokeWidth]; + self.strokePath = CGPathRetain((CGPathRef)CFAutorelease(CGPathCreateCopyByStrokingPath( + path, nil, width, self.strokeLinecap, self.strokeLinejoin, self.strokeMiterlimit))); + // TODO add dashing + // CGPathCreateCopyByDashingPath(CGPathRef _Nullable path, const CGAffineTransform * _Nullable transform, CGFloat + // phase, const CGFloat * _Nullable lengths, size_t count) + } } - (BOOL)isUserInteractionEnabled { - return NO; + return NO; } // hitTest delegate - (RNSVGPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { - if (!_hitArea) { - return nil; + if (!_hitArea) { + return nil; + } + + BOOL canReceiveTouchEvents = (self.pointerEvents != RCTPointerEventsNone && ![self isHidden]); + if (!canReceiveTouchEvents) { + return nil; + } + + if (self.active) { + if (!event) { + self.active = NO; } - - BOOL canReceiveTouchEvents = (self.pointerEvents != RCTPointerEventsNone && ![self isHidden]); - if(!canReceiveTouchEvents) { - return nil; - } - - if (self.active) { - if (!event) { - self.active = NO; - } - return self; - } - - CGPoint transformed = CGPointApplyAffineTransform(point, self.invmatrix); - transformed = CGPointApplyAffineTransform(transformed, self.invTransform); - - if (!CGRectContainsPoint(self.pathBounds, transformed) && - !CGRectContainsPoint(self.markerBounds, transformed)) { - return nil; - } - - BOOL evenodd = self.fillRule == kRNSVGCGFCRuleEvenodd; - if (!CGPathContainsPoint(_hitArea, nil, transformed, evenodd) && - !CGPathContainsPoint(self.strokePath, nil, transformed, NO) && - !CGPathContainsPoint(self.markerPath, nil, transformed, NO)) { - return nil; - } - - if (self.clipPath) { - RNSVGClipPath *clipNode = (RNSVGClipPath*)[self.svgView getDefinedClipPath:self.clipPath]; - if ([clipNode isSimpleClipPath]) { - CGPathRef clipPath = [self getClipPath]; - if (clipPath && !CGPathContainsPoint(clipPath, nil, transformed, clipNode.clipRule == kRNSVGCGFCRuleEvenodd)) { - return nil; - } - } else { - RNSVGRenderable *clipGroup = (RNSVGRenderable*)clipNode; - if (![clipGroup hitTest:transformed withEvent:event]) { - return nil; - } - } - } - return self; + } + + CGPoint transformed = CGPointApplyAffineTransform(point, self.invmatrix); + transformed = CGPointApplyAffineTransform(transformed, self.invTransform); + + if (!CGRectContainsPoint(self.pathBounds, transformed) && !CGRectContainsPoint(self.markerBounds, transformed)) { + return nil; + } + + BOOL evenodd = self.fillRule == kRNSVGCGFCRuleEvenodd; + if (!CGPathContainsPoint(_hitArea, nil, transformed, evenodd) && + !CGPathContainsPoint(self.strokePath, nil, transformed, NO) && + !CGPathContainsPoint(self.markerPath, nil, transformed, NO)) { + return nil; + } + + if (self.clipPath) { + RNSVGClipPath *clipNode = (RNSVGClipPath *)[self.svgView getDefinedClipPath:self.clipPath]; + if ([clipNode isSimpleClipPath]) { + CGPathRef clipPath = [self getClipPath]; + if (clipPath && !CGPathContainsPoint(clipPath, nil, transformed, clipNode.clipRule == kRNSVGCGFCRuleEvenodd)) { + return nil; + } + } else { + RNSVGRenderable *clipGroup = (RNSVGRenderable *)clipNode; + if (![clipGroup hitTest:transformed withEvent:event]) { + return nil; + } + } + } + + return self; } - (NSArray *)getAttributeList { - return _attributeList; + return _attributeList; } - (void)mergeProperties:(__kindof RNSVGRenderable *)target { - NSArray *targetAttributeList = [target getAttributeList]; + NSArray *targetAttributeList = [target getAttributeList]; - if (targetAttributeList.count == 0) { - return; + if (targetAttributeList.count == 0) { + return; + } + self.merging = true; + + NSMutableArray *attributeList = self.propList ? [self.propList mutableCopy] : [[NSMutableArray alloc] init]; + _originProperties = [[NSMutableDictionary alloc] init]; + + for (NSString *key in targetAttributeList) { + [_originProperties setValue:[self valueForKey:key] forKey:key]; + if (![attributeList containsObject:key]) { + [attributeList addObject:key]; + [self setValue:[target valueForKey:key] forKey:key]; } - self.merging = true; + } - NSMutableArray* attributeList = self.propList ? [self.propList mutableCopy] : [[NSMutableArray alloc] init]; - _originProperties = [[NSMutableDictionary alloc] init]; - - for (NSString *key in targetAttributeList) { - [_originProperties setValue:[self valueForKey:key] forKey:key]; - if (![attributeList containsObject:key]) { - [attributeList addObject:key]; - [self setValue:[target valueForKey:key] forKey:key]; - } - } - - _lastMergedList = targetAttributeList; - _attributeList = [attributeList copy]; - self.merging = false; + _lastMergedList = targetAttributeList; + _attributeList = [attributeList copy]; + self.merging = false; } - (void)resetProperties { - self.merging = true; - for (NSString *key in _lastMergedList) { - [self setValue:[_originProperties valueForKey:key] forKey:key]; - } + self.merging = true; + for (NSString *key in _lastMergedList) { + [self setValue:[_originProperties valueForKey:key] forKey:key]; + } - _lastMergedList = nil; - _attributeList = _propList; - self.merging = false; + _lastMergedList = nil; + _attributeList = _propList; + self.merging = false; } @end diff --git a/apple/RNSVGUIKit.h b/apple/RNSVGUIKit.h index ba6ed65f..3562fe46 100644 --- a/apple/RNSVGUIKit.h +++ b/apple/RNSVGUIKit.h @@ -1,5 +1,5 @@ -// Most (if not all) of this file could probably go away once react-native-macos's version of RCTUIKit.h makes its way upstream. -// https://github.com/microsoft/react-native-macos/issues/242 +// Most (if not all) of this file could probably go away once react-native-macos's version of RCTUIKit.h makes its way +// upstream. https://github.com/microsoft/react-native-macos/issues/242 #if !TARGET_OS_OSX @@ -12,9 +12,9 @@ #else // TARGET_OS_OSX [ -// Due to name mangling, calling c-style functions from .mm files will fail, therefore we need to wrap them with extern "C" -// so they are handled correctly. We also need to have imports positioned in a correct way, -// so that this extern "C" wrapper is used before the functions from RCTUIKit are used. +// Due to name mangling, calling c-style functions from .mm files will fail, therefore we need to wrap them with extern +// "C" so they are handled correctly. We also need to have imports positioned in a correct way, so that this extern "C" +// wrapper is used before the functions from RCTUIKit are used. #ifdef __cplusplus extern "C" { #endif @@ -37,7 +37,8 @@ extern "C" { @end // TODO: These could probably be a part of react-native-macos -// See https://github.com/microsoft/react-native-macos/issues/658 and https://github.com/microsoft/react-native-macos/issues/659 +// See https://github.com/microsoft/react-native-macos/issues/658 and +// https://github.com/microsoft/react-native-macos/issues/659 @interface NSImage (RNSVGMacOSExtensions) @property (readonly) CGImageRef CGImage; @end diff --git a/apple/RNSVGUIKit.macos.mm b/apple/RNSVGUIKit.macos.mm index 1be98398..c4484ff1 100644 --- a/apple/RNSVGUIKit.macos.mm +++ b/apple/RNSVGUIKit.macos.mm @@ -1,86 +1,83 @@ #import "RNSVGUIKit.h" -@implementation RNSVGView -{ - NSColor *_tintColor; +@implementation RNSVGView { + NSColor *_tintColor; } - (CGPoint)center { - NSRect frameRect = self.frame; - CGFloat xCenter = frameRect.origin.x + frameRect.size.width / 2; - CGFloat yCenter = frameRect.origin.y + frameRect.size.height / 2; - return CGPointMake(xCenter, yCenter); + NSRect frameRect = self.frame; + CGFloat xCenter = frameRect.origin.x + frameRect.size.width / 2; + CGFloat yCenter = frameRect.origin.y + frameRect.size.height / 2; + return CGPointMake(xCenter, yCenter); } - (void)setCenter:(CGPoint)point { - NSRect frameRect = self.frame; - CGFloat xOrigin = frameRect.origin.x - frameRect.size.width / 2; - CGFloat yOrigin = frameRect.origin.y - frameRect.size.height / 2; - self.frame = CGRectMake(xOrigin, yOrigin, frameRect.size.width, frameRect.size.height); + NSRect frameRect = self.frame; + CGFloat xOrigin = frameRect.origin.x - frameRect.size.width / 2; + CGFloat yOrigin = frameRect.origin.y - frameRect.size.height / 2; + self.frame = CGRectMake(xOrigin, yOrigin, frameRect.size.width, frameRect.size.height); } - (NSColor *)tintColor { - if (_tintColor != nil) { - return _tintColor; - } + if (_tintColor != nil) { + return _tintColor; + } - // To mimic iOS's tintColor, we crawl up the view hierarchy until either: - // (a) we find a valid color - // (b) we reach a view that isn't an RNSVGView - NSView *parentView = [self superview]; - if ([parentView isKindOfClass:[RNSVGView class]]) { - return [(RNSVGView *)parentView tintColor]; - } else { - return [NSColor controlAccentColor]; - } + // To mimic iOS's tintColor, we crawl up the view hierarchy until either: + // (a) we find a valid color + // (b) we reach a view that isn't an RNSVGView + NSView *parentView = [self superview]; + if ([parentView isKindOfClass:[RNSVGView class]]) { + return [(RNSVGView *)parentView tintColor]; + } else { + return [NSColor controlAccentColor]; + } } - (void)setTintColor:(NSColor *)tintColor { - _tintColor = tintColor; - [self setNeedsDisplay:YES]; + _tintColor = tintColor; + [self setNeedsDisplay:YES]; } @end - @implementation NSImage (RNSVGMacOSExtensions) -- (CGImageRef) CGImage +- (CGImageRef)CGImage { - return [self CGImageForProposedRect:NULL context:NULL hints:NULL]; + return [self CGImageForProposedRect:NULL context:NULL hints:NULL]; } @end - @implementation NSValue (RNSVGMacOSExtensions) + (NSValue *)valueWithCGAffineTransform:(CGAffineTransform)transform { - return [NSValue valueWithBytes:&transform objCType:@encode(CGAffineTransform)]; + return [NSValue valueWithBytes:&transform objCType:@encode(CGAffineTransform)]; } + (NSValue *)valueWithCGPoint:(CGPoint)point { - return [NSValue valueWithBytes:&point objCType:@encode(CGPoint)]; + return [NSValue valueWithBytes:&point objCType:@encode(CGPoint)]; } - (CGAffineTransform)CGAffineTransformValue { - CGAffineTransform value; - [self getValue:&value]; - return value; + CGAffineTransform value; + [self getValue:&value]; + return value; } - (CGPoint)CGPointValue { - CGPoint value; - [self getValue:&value]; - return value; + CGPoint value; + [self getValue:&value]; + return value; } @end diff --git a/apple/Shapes/RNSVGCircle.h b/apple/Shapes/RNSVGCircle.h index f4e411a8..9ab06ded 100644 --- a/apple/Shapes/RNSVGCircle.h +++ b/apple/Shapes/RNSVGCircle.h @@ -12,9 +12,8 @@ @interface RNSVGCircle : RNSVGRenderable -@property (nonatomic, strong) RNSVGLength* cx; -@property (nonatomic, strong) RNSVGLength* cy; -@property (nonatomic, strong) RNSVGLength* r; +@property (nonatomic, strong) RNSVGLength *cx; +@property (nonatomic, strong) RNSVGLength *cy; +@property (nonatomic, strong) RNSVGLength *r; @end - diff --git a/apple/Shapes/RNSVGCircle.mm b/apple/Shapes/RNSVGCircle.mm index 8e8dc204..4ce5b236 100644 --- a/apple/Shapes/RNSVGCircle.mm +++ b/apple/Shapes/RNSVGCircle.mm @@ -11,9 +11,9 @@ #ifdef RN_FABRIC_ENABLED #import -#import "RCTFabricComponentsPlugins.h" -#import "RCTConversions.h" #import +#import "RCTConversions.h" +#import "RCTFabricComponentsPlugins.h" #import "RNSVGFabricConversions.h" #endif // RN_FABRIC_ENABLED @@ -40,59 +40,59 @@ using namespace facebook::react; - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps { - const auto &newProps = *std::static_pointer_cast(props); + const auto &newProps = *std::static_pointer_cast(props); - self.cx = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.cx)]; - self.cy = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.cy)]; - self.r = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.r)]; - - setCommonRenderableProps(newProps, self); + self.cx = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.cx)]; + self.cy = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.cy)]; + self.r = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.r)]; + + setCommonRenderableProps(newProps, self); } - (void)prepareForRecycle { - [super prepareForRecycle]; - _cx = nil; - _cy = nil; - _r = nil; + [super prepareForRecycle]; + _cx = nil; + _cy = nil; + _r = nil; } #endif // RN_FABRIC_ENABLED - (void)setCx:(RNSVGLength *)cx { - if ([cx isEqualTo:_cx]) { - return; - } - [self invalidate]; - _cx = cx; + if ([cx isEqualTo:_cx]) { + return; + } + [self invalidate]; + _cx = cx; } - (void)setCy:(RNSVGLength *)cy { - if ([cy isEqualTo:_cy]) { - return; - } - [self invalidate]; - _cy = cy; + if ([cy isEqualTo:_cy]) { + return; + } + [self invalidate]; + _cy = cy; } - (void)setR:(RNSVGLength *)r { - if ([r isEqualTo:_r]) { - return; - } - [self invalidate]; - _r = r; + if ([r isEqualTo:_r]) { + return; + } + [self invalidate]; + _r = r; } - (CGPathRef)getPath:(CGContextRef)context { - CGMutablePathRef path = CGPathCreateMutable(); - CGFloat cx = [self relativeOnWidth:self.cx]; - CGFloat cy = [self relativeOnHeight:self.cy]; - CGFloat r = [self relativeOnOther:self.r]; - CGPathAddArc(path, nil, cx, cy, r, 0, 2 * (CGFloat)M_PI, NO); - return (CGPathRef)CFAutorelease(path); + CGMutablePathRef path = CGPathCreateMutable(); + CGFloat cx = [self relativeOnWidth:self.cx]; + CGFloat cy = [self relativeOnHeight:self.cy]; + CGFloat r = [self relativeOnOther:self.r]; + CGPathAddArc(path, nil, cx, cy, r, 0, 2 * (CGFloat)M_PI, NO); + return (CGPathRef)CFAutorelease(path); } @end diff --git a/apple/Shapes/RNSVGEllipse.h b/apple/Shapes/RNSVGEllipse.h index 8ca56cca..79ef7793 100644 --- a/apple/Shapes/RNSVGEllipse.h +++ b/apple/Shapes/RNSVGEllipse.h @@ -11,8 +11,8 @@ #import "RNSVGPath.h" @interface RNSVGEllipse : RNSVGRenderable -@property (nonatomic, strong) RNSVGLength* cx; -@property (nonatomic, strong) RNSVGLength* cy; -@property (nonatomic, strong) RNSVGLength* rx; -@property (nonatomic, strong) RNSVGLength* ry; +@property (nonatomic, strong) RNSVGLength *cx; +@property (nonatomic, strong) RNSVGLength *cy; +@property (nonatomic, strong) RNSVGLength *rx; +@property (nonatomic, strong) RNSVGLength *ry; @end diff --git a/apple/Shapes/RNSVGEllipse.mm b/apple/Shapes/RNSVGEllipse.mm index 71800a27..a9b22bf7 100644 --- a/apple/Shapes/RNSVGEllipse.mm +++ b/apple/Shapes/RNSVGEllipse.mm @@ -11,9 +11,9 @@ #ifdef RN_FABRIC_ENABLED #import -#import "RCTFabricComponentsPlugins.h" -#import "RCTConversions.h" #import +#import "RCTConversions.h" +#import "RCTFabricComponentsPlugins.h" #import "RNSVGFabricConversions.h" #endif // RN_FABRIC_ENABLED @@ -40,71 +40,71 @@ using namespace facebook::react; - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps { - const auto &newProps = *std::static_pointer_cast(props); + const auto &newProps = *std::static_pointer_cast(props); - self.cx = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.cx)]; - self.cy = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.cy)]; - self.rx = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.rx)]; - self.ry = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.ry)]; + self.cx = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.cx)]; + self.cy = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.cy)]; + self.rx = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.rx)]; + self.ry = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.ry)]; - setCommonRenderableProps(newProps, self); + setCommonRenderableProps(newProps, self); } - (void)prepareForRecycle { - [super prepareForRecycle]; - _cx = nil; - _cy = nil; - _rx = nil; - _ry = nil; + [super prepareForRecycle]; + _cx = nil; + _cy = nil; + _rx = nil; + _ry = nil; } #endif // RN_FABRIC_ENABLED - (void)setCx:(RNSVGLength *)cx { - if ([cx isEqualTo:_cx]) { - return; - } - [self invalidate]; - _cx = cx; + if ([cx isEqualTo:_cx]) { + return; + } + [self invalidate]; + _cx = cx; } - (void)setCy:(RNSVGLength *)cy { - if ([cy isEqualTo:_cy]) { - return; - } - [self invalidate]; - _cy = cy; + if ([cy isEqualTo:_cy]) { + return; + } + [self invalidate]; + _cy = cy; } - (void)setRx:(RNSVGLength *)rx { - if ([rx isEqualTo:_rx]) { - return; - } - [self invalidate]; - _rx = rx; + if ([rx isEqualTo:_rx]) { + return; + } + [self invalidate]; + _rx = rx; } - (void)setRy:(RNSVGLength *)ry { - if ([ry isEqualTo:_ry]) { - return; - } - [self invalidate]; - _ry = ry; + if ([ry isEqualTo:_ry]) { + return; + } + [self invalidate]; + _ry = ry; } - (CGPathRef)getPath:(CGContextRef)context { - CGMutablePathRef path = CGPathCreateMutable(); - CGFloat cx = [self relativeOnWidth:self.cx]; - CGFloat cy = [self relativeOnHeight:self.cy]; - CGFloat rx = [self relativeOnWidth:self.rx]; - CGFloat ry = [self relativeOnHeight:self.ry]; - CGPathAddEllipseInRect(path, nil, CGRectMake(cx - rx, cy - ry, rx * 2, ry * 2)); - return (CGPathRef)CFAutorelease(path); + CGMutablePathRef path = CGPathCreateMutable(); + CGFloat cx = [self relativeOnWidth:self.cx]; + CGFloat cy = [self relativeOnHeight:self.cy]; + CGFloat rx = [self relativeOnWidth:self.rx]; + CGFloat ry = [self relativeOnHeight:self.ry]; + CGPathAddEllipseInRect(path, nil, CGRectMake(cx - rx, cy - ry, rx * 2, ry * 2)); + return (CGPathRef)CFAutorelease(path); } @end diff --git a/apple/Shapes/RNSVGLine.h b/apple/Shapes/RNSVGLine.h index 1183902d..cd648f25 100644 --- a/apple/Shapes/RNSVGLine.h +++ b/apple/Shapes/RNSVGLine.h @@ -11,8 +11,8 @@ #import "RNSVGPath.h" @interface RNSVGLine : RNSVGRenderable -@property (nonatomic, strong) RNSVGLength* x1; -@property (nonatomic, strong) RNSVGLength* y1; -@property (nonatomic, strong) RNSVGLength* x2; -@property (nonatomic, strong) RNSVGLength* y2; +@property (nonatomic, strong) RNSVGLength *x1; +@property (nonatomic, strong) RNSVGLength *y1; +@property (nonatomic, strong) RNSVGLength *x2; +@property (nonatomic, strong) RNSVGLength *y2; @end diff --git a/apple/Shapes/RNSVGLine.mm b/apple/Shapes/RNSVGLine.mm index 24ae761f..03886f64 100644 --- a/apple/Shapes/RNSVGLine.mm +++ b/apple/Shapes/RNSVGLine.mm @@ -11,9 +11,9 @@ #ifdef RN_FABRIC_ENABLED #import -#import "RCTFabricComponentsPlugins.h" -#import "RCTConversions.h" #import +#import "RCTConversions.h" +#import "RCTFabricComponentsPlugins.h" #import "RNSVGFabricConversions.h" #endif // RN_FABRIC_ENABLED @@ -40,73 +40,73 @@ using namespace facebook::react; - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps { - const auto &newProps = *std::static_pointer_cast(props); + const auto &newProps = *std::static_pointer_cast(props); - self.x1 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x1)]; - self.y1 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y1)]; - self.x2 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x2)]; - self.y2 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y2)]; + self.x1 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x1)]; + self.y1 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y1)]; + self.x2 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x2)]; + self.y2 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y2)]; - setCommonRenderableProps(newProps, self); + setCommonRenderableProps(newProps, self); } - (void)prepareForRecycle { - [super prepareForRecycle]; - _x1 = nil; - _y1 = nil; - _x2 = nil; - _y2 = nil; + [super prepareForRecycle]; + _x1 = nil; + _y1 = nil; + _x2 = nil; + _y2 = nil; } #endif // RN_FABRIC_ENABLED - (void)setX1:(RNSVGLength *)x1 { - if ([x1 isEqualTo:_x1]) { - return; - } - [self invalidate]; - _x1 = x1; + if ([x1 isEqualTo:_x1]) { + return; + } + [self invalidate]; + _x1 = x1; } - (void)setY1:(RNSVGLength *)y1 { - if ([y1 isEqualTo:_y1]) { - return; - } - [self invalidate]; - _y1 = y1; + if ([y1 isEqualTo:_y1]) { + return; + } + [self invalidate]; + _y1 = y1; } - (void)setX2:(RNSVGLength *)x2 { - if ([x2 isEqualTo:_x2]) { - return; - } - [self invalidate]; - _x2 = x2; + if ([x2 isEqualTo:_x2]) { + return; + } + [self invalidate]; + _x2 = x2; } - (void)setY2:(RNSVGLength *)y2 { - if ([y2 isEqualTo:_y2]) { - return; - } - [self invalidate]; - _y2 = y2; + if ([y2 isEqualTo:_y2]) { + return; + } + [self invalidate]; + _y2 = y2; } - (CGPathRef)getPath:(CGContextRef)context { - CGMutablePathRef path = CGPathCreateMutable(); - CGFloat x1 = [self relativeOnWidth:self.x1]; - CGFloat y1 = [self relativeOnHeight:self.y1]; - CGFloat x2 = [self relativeOnWidth:self.x2]; - CGFloat y2 = [self relativeOnHeight:self.y2]; - CGPathMoveToPoint(path, nil, x1, y1); - CGPathAddLineToPoint(path, nil, x2, y2); - - return (CGPathRef)CFAutorelease(path); + CGMutablePathRef path = CGPathCreateMutable(); + CGFloat x1 = [self relativeOnWidth:self.x1]; + CGFloat y1 = [self relativeOnHeight:self.y1]; + CGFloat x2 = [self relativeOnWidth:self.x2]; + CGFloat y2 = [self relativeOnHeight:self.y2]; + CGPathMoveToPoint(path, nil, x1, y1); + CGPathAddLineToPoint(path, nil, x2, y2); + + return (CGPathRef)CFAutorelease(path); } @end diff --git a/apple/Shapes/RNSVGRect.h b/apple/Shapes/RNSVGRect.h index 04eea2c6..97a47dca 100644 --- a/apple/Shapes/RNSVGRect.h +++ b/apple/Shapes/RNSVGRect.h @@ -12,11 +12,11 @@ @interface RNSVGRect : RNSVGRenderable -@property (nonatomic, strong) RNSVGLength* x; -@property (nonatomic, strong) RNSVGLength* y; -@property (nonatomic, strong) RNSVGLength* rectwidth; -@property (nonatomic, strong) RNSVGLength* rectheight; -@property (nonatomic, strong) RNSVGLength* rx; -@property (nonatomic, strong) RNSVGLength* ry; +@property (nonatomic, strong) RNSVGLength *x; +@property (nonatomic, strong) RNSVGLength *y; +@property (nonatomic, strong) RNSVGLength *rectwidth; +@property (nonatomic, strong) RNSVGLength *rectheight; +@property (nonatomic, strong) RNSVGLength *rx; +@property (nonatomic, strong) RNSVGLength *ry; @end diff --git a/apple/Shapes/RNSVGRect.mm b/apple/Shapes/RNSVGRect.mm index 1c0c7351..4a6dc02d 100644 --- a/apple/Shapes/RNSVGRect.mm +++ b/apple/Shapes/RNSVGRect.mm @@ -11,9 +11,9 @@ #ifdef RN_FABRIC_ENABLED #import -#import "RCTFabricComponentsPlugins.h" -#import "RCTConversions.h" #import +#import "RCTConversions.h" +#import "RCTFabricComponentsPlugins.h" #import "RNSVGFabricConversions.h" #endif // RN_FABRIC_ENABLED @@ -40,132 +40,132 @@ using namespace facebook::react; - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps { - const auto &newProps = *std::static_pointer_cast(props); + const auto &newProps = *std::static_pointer_cast(props); - self.x = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x)]; - self.y = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y)]; - if (RCTNSStringFromStringNilIfEmpty(newProps.rectheight)) { - self.rectheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.rectheight)]; - } - if (RCTNSStringFromStringNilIfEmpty(newProps.rectwidth)) { - self.rectwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.rectwidth)]; - } - if (RCTNSStringFromStringNilIfEmpty(newProps.height)) { - self.rectheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.height)]; - } - if (RCTNSStringFromStringNilIfEmpty(newProps.width)) { - self.rectwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.width)]; - } - self.rx = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.rx)]; - self.ry = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.ry)]; + self.x = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x)]; + self.y = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y)]; + if (RCTNSStringFromStringNilIfEmpty(newProps.rectheight)) { + self.rectheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.rectheight)]; + } + if (RCTNSStringFromStringNilIfEmpty(newProps.rectwidth)) { + self.rectwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.rectwidth)]; + } + if (RCTNSStringFromStringNilIfEmpty(newProps.height)) { + self.rectheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.height)]; + } + if (RCTNSStringFromStringNilIfEmpty(newProps.width)) { + self.rectwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.width)]; + } + self.rx = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.rx)]; + self.ry = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.ry)]; - setCommonRenderableProps(newProps, self); + setCommonRenderableProps(newProps, self); } - (void)prepareForRecycle { - [super prepareForRecycle]; - - _x = nil; - _y = nil; - _rectwidth = nil; - _rectheight = nil; - _rx = nil; - _ry = nil; + [super prepareForRecycle]; + + _x = nil; + _y = nil; + _rectwidth = nil; + _rectheight = nil; + _rx = nil; + _ry = nil; } #endif // RN_FABRIC_ENABLED - (void)setX:(RNSVGLength *)x { - if ([x isEqualTo:_x]) { - return; - } - [self invalidate]; - _x = x; + if ([x isEqualTo:_x]) { + return; + } + [self invalidate]; + _x = x; } - (void)setY:(RNSVGLength *)y { - if ([y isEqualTo:_y]) { - return; - } - [self invalidate]; - _y = y; + if ([y isEqualTo:_y]) { + return; + } + [self invalidate]; + _y = y; } - (void)setRectwidth:(RNSVGLength *)rectwidth { - if ([rectwidth isEqualTo:_rectwidth]) { - return; - } - [self invalidate]; - _rectwidth = rectwidth; + if ([rectwidth isEqualTo:_rectwidth]) { + return; + } + [self invalidate]; + _rectwidth = rectwidth; } - (void)setRectheight:(RNSVGLength *)rectheight { - if ([rectheight isEqualTo:_rectheight]) { - return; - } - [self invalidate]; - _rectheight = rectheight; + if ([rectheight isEqualTo:_rectheight]) { + return; + } + [self invalidate]; + _rectheight = rectheight; } - (void)setRx:(RNSVGLength *)rx { - if ([rx isEqualTo:_rx]) { - return; - } - [self invalidate]; - _rx = rx; + if ([rx isEqualTo:_rx]) { + return; + } + [self invalidate]; + _rx = rx; } - (void)setRy:(RNSVGLength *)ry { - if ([ry isEqualTo:_ry]) { - return; - } - [self invalidate]; - _ry = ry; + if ([ry isEqualTo:_ry]) { + return; + } + [self invalidate]; + _ry = ry; } - (CGPathRef)getPath:(CGContextRef)context { - CGMutablePathRef path = CGPathCreateMutable(); - CGFloat x = [self relativeOnWidth:self.x]; - CGFloat y = [self relativeOnHeight:self.y]; - CGFloat width = [self relativeOnWidth:self.rectwidth]; - CGFloat height = [self relativeOnHeight:self.rectheight]; + CGMutablePathRef path = CGPathCreateMutable(); + CGFloat x = [self relativeOnWidth:self.x]; + CGFloat y = [self relativeOnHeight:self.y]; + CGFloat width = [self relativeOnWidth:self.rectwidth]; + CGFloat height = [self relativeOnHeight:self.rectheight]; - if (self.rx != nil || self.ry != nil) { - CGFloat rx = 0; - CGFloat ry = 0; - if (self.rx == nil) { - ry = [self relativeOnHeight:self.ry]; - rx = ry; - } else if (self.ry == nil) { - rx = [self relativeOnWidth:self.rx]; - ry = rx; - } else { - rx = [self relativeOnWidth:self.rx]; - ry = [self relativeOnHeight:self.ry]; - } - - if (rx > width / 2) { - rx = width / 2; - } - - if (ry > height / 2) { - ry = height / 2; - } - - CGPathAddRoundedRect(path, nil, CGRectMake(x, y, width, height), rx, ry); + if (self.rx != nil || self.ry != nil) { + CGFloat rx = 0; + CGFloat ry = 0; + if (self.rx == nil) { + ry = [self relativeOnHeight:self.ry]; + rx = ry; + } else if (self.ry == nil) { + rx = [self relativeOnWidth:self.rx]; + ry = rx; } else { - CGPathAddRect(path, nil, CGRectMake(x, y, width, height)); + rx = [self relativeOnWidth:self.rx]; + ry = [self relativeOnHeight:self.ry]; } - return (CGPathRef)CFAutorelease(path); + if (rx > width / 2) { + rx = width / 2; + } + + if (ry > height / 2) { + ry = height / 2; + } + + CGPathAddRoundedRect(path, nil, CGRectMake(x, y, width, height), rx, ry); + } else { + CGPathAddRect(path, nil, CGRectMake(x, y, width, height)); + } + + return (CGPathRef)CFAutorelease(path); } @end diff --git a/apple/Text/RNSVGFontData.h b/apple/Text/RNSVGFontData.h index 35ab92c7..2b8a5bef 100644 --- a/apple/Text/RNSVGFontData.h +++ b/apple/Text/RNSVGFontData.h @@ -2,35 +2,33 @@ #import "RNSVGUIKit.h" -#import "RNSVGTextProperties.h" #import "RNSVGPropHelper.h" +#import "RNSVGTextProperties.h" @interface RNSVGFontData : NSObject { -@public - CGFloat fontSize; - NSString * fontSize_; - NSString *fontFamily; - enum RNSVGFontStyle fontStyle; - NSDictionary * fontData; - enum RNSVGFontWeight fontWeight; - int absoluteFontWeight; - NSString *fontFeatureSettings; - enum RNSVGFontVariantLigatures fontVariantLigatures; - enum RNSVGTextAnchor textAnchor; - enum RNSVGTextDecoration textDecoration; - CGFloat kerning; - CGFloat wordSpacing; - CGFloat letterSpacing; - bool manualKerning; + @public + CGFloat fontSize; + NSString *fontSize_; + NSString *fontFamily; + enum RNSVGFontStyle fontStyle; + NSDictionary *fontData; + enum RNSVGFontWeight fontWeight; + int absoluteFontWeight; + NSString *fontFeatureSettings; + enum RNSVGFontVariantLigatures fontVariantLigatures; + enum RNSVGTextAnchor textAnchor; + enum RNSVGTextDecoration textDecoration; + CGFloat kerning; + CGFloat wordSpacing; + CGFloat letterSpacing; + bool manualKerning; } + (instancetype)Defaults; -+ (CGFloat)toAbsoluteWithNSString:(NSString *)string - fontSize:(CGFloat)fontSize; ++ (CGFloat)toAbsoluteWithNSString:(NSString *)string fontSize:(CGFloat)fontSize; -+ (instancetype)initWithNSDictionary:(NSDictionary *)font - parent:(RNSVGFontData *)parent; ++ (instancetype)initWithNSDictionary:(NSDictionary *)font parent:(RNSVGFontData *)parent; @end diff --git a/apple/Text/RNSVGFontData.mm b/apple/Text/RNSVGFontData.mm index 248d82bf..318156bc 100644 --- a/apple/Text/RNSVGFontData.mm +++ b/apple/Text/RNSVGFontData.mm @@ -1,7 +1,7 @@ #import "RNSVGFontData.h" +#import "RNSVGNode.h" #import "RNSVGPropHelper.h" #import "RNSVGTextProperties.h" -#import "RNSVGNode.h" #define RNSVG_DEFAULT_KERNING 0.0 #define RNSVG_DEFAULT_WORD_SPACING 0.0 @@ -23,178 +23,174 @@ RNSVGFontData *RNSVGFontData_Defaults; @implementation RNSVGFontData -+ (instancetype)Defaults { - if (!RNSVGFontData_Defaults) { - RNSVGFontData *self = [RNSVGFontData alloc]; - self->fontData = nil; - self->fontFamily = @""; - self->fontStyle = RNSVGFontStyleNormal; - self->fontWeight = RNSVGFontWeightNormal; - self->absoluteFontWeight = 400; - self->fontFeatureSettings = @""; - self->fontVariantLigatures = RNSVGFontVariantLigaturesNormal; - self->textAnchor = RNSVGTextAnchorStart; - self->textDecoration = RNSVGTextDecorationNone; - self->manualKerning = false; - self->kerning = RNSVG_DEFAULT_KERNING; - self->fontSize = RNSVG_DEFAULT_FONT_SIZE; - self->wordSpacing = RNSVG_DEFAULT_WORD_SPACING; - self->letterSpacing = RNSVG_DEFAULT_LETTER_SPACING; - RNSVGFontData_Defaults = self; - } - return RNSVGFontData_Defaults; ++ (instancetype)Defaults +{ + if (!RNSVGFontData_Defaults) { + RNSVGFontData *self = [RNSVGFontData alloc]; + self->fontData = nil; + self->fontFamily = @""; + self->fontStyle = RNSVGFontStyleNormal; + self->fontWeight = RNSVGFontWeightNormal; + self->absoluteFontWeight = 400; + self->fontFeatureSettings = @""; + self->fontVariantLigatures = RNSVGFontVariantLigaturesNormal; + self->textAnchor = RNSVGTextAnchorStart; + self->textDecoration = RNSVGTextDecorationNone; + self->manualKerning = false; + self->kerning = RNSVG_DEFAULT_KERNING; + self->fontSize = RNSVG_DEFAULT_FONT_SIZE; + self->wordSpacing = RNSVG_DEFAULT_WORD_SPACING; + self->letterSpacing = RNSVG_DEFAULT_LETTER_SPACING; + RNSVGFontData_Defaults = self; + } + return RNSVGFontData_Defaults; } -+ (CGFloat)toAbsoluteWithNSString:(NSString *)string - fontSize:(CGFloat)fontSize { - return [RNSVGPropHelper fromRelativeWithNSString:string - relative:0 - fontSize:fontSize]; ++ (CGFloat)toAbsoluteWithNSString:(NSString *)string fontSize:(CGFloat)fontSize +{ + return [RNSVGPropHelper fromRelativeWithNSString:string relative:0 fontSize:fontSize]; } -- (void)setInheritedWeight:(RNSVGFontData*) parent { - absoluteFontWeight = parent->absoluteFontWeight; - fontWeight = parent->fontWeight; +- (void)setInheritedWeight:(RNSVGFontData *)parent +{ + absoluteFontWeight = parent->absoluteFontWeight; + fontWeight = parent->fontWeight; } -RNSVGFontWeight nearestFontWeight(int absoluteFontWeight) { - return RNSVGFontWeights[(int)round(absoluteFontWeight / 100.0)]; +RNSVGFontWeight nearestFontWeight(int absoluteFontWeight) +{ + return RNSVGFontWeights[(int)round(absoluteFontWeight / 100.0)]; } -- (void)handleNumericWeight:(RNSVGFontData*)parent weight:(double)weight { - long roundWeight = round(weight); - if (roundWeight >= 1 && roundWeight <= 1000) { - absoluteFontWeight = (int)roundWeight; - fontWeight = nearestFontWeight(absoluteFontWeight); - } else { - [self setInheritedWeight:parent]; - } +- (void)handleNumericWeight:(RNSVGFontData *)parent weight:(double)weight +{ + long roundWeight = round(weight); + if (roundWeight >= 1 && roundWeight <= 1000) { + absoluteFontWeight = (int)roundWeight; + fontWeight = nearestFontWeight(absoluteFontWeight); + } else { + [self setInheritedWeight:parent]; + } } // https://drafts.csswg.org/css-fonts-4/#relative-weights -int AbsoluteFontWeight(RNSVGFontWeight fontWeight, RNSVGFontData* parent) { - if (fontWeight == RNSVGFontWeightBolder) { - return bolder(parent->absoluteFontWeight); - } else if (fontWeight == RNSVGFontWeightLighter) { - return lighter(parent->absoluteFontWeight); - } else { - return RNSVGAbsoluteFontWeights[fontWeight]; - } +int AbsoluteFontWeight(RNSVGFontWeight fontWeight, RNSVGFontData *parent) +{ + if (fontWeight == RNSVGFontWeightBolder) { + return bolder(parent->absoluteFontWeight); + } else if (fontWeight == RNSVGFontWeightLighter) { + return lighter(parent->absoluteFontWeight); + } else { + return RNSVGAbsoluteFontWeights[fontWeight]; + } } -int bolder(int inherited) { - if (inherited < 350) { - return 400; - } else if (inherited < 550) { - return 700; - } else if (inherited < 900) { - return 900; - } else { - return inherited; - } +int bolder(int inherited) +{ + if (inherited < 350) { + return 400; + } else if (inherited < 550) { + return 700; + } else if (inherited < 900) { + return 900; + } else { + return inherited; + } } -int lighter(int inherited) { - if (inherited < 100) { - return inherited; - } else if (inherited < 550) { - return 100; - } else if (inherited < 750) { - return 400; - } else { - return 700; - } +int lighter(int inherited) +{ + if (inherited < 100) { + return inherited; + } else if (inherited < 550) { + return 100; + } else if (inherited < 750) { + return 400; + } else { + return 700; + } } -+ (instancetype)initWithNSDictionary:(NSDictionary *)font - parent:(RNSVGFontData *)parent { - RNSVGFontData *data = [RNSVGFontData alloc]; - CGFloat parentFontSize = parent->fontSize; - if ([font objectForKey:FONT_SIZE]) { - id fontSize = [font objectForKey:FONT_SIZE]; - if ([fontSize isKindOfClass:NSNumber.class]) { - NSNumber* fs = fontSize; - data->fontSize = (CGFloat)[fs doubleValue]; - } else { - data->fontSize = [RNSVGPropHelper fromRelativeWithNSString:fontSize - relative:parentFontSize - fontSize:parentFontSize]; - } - } - else { - data->fontSize = parentFontSize; - } - - if ([font objectForKey:FONT_WEIGHT]) { - id fontWeight = [font objectForKey:FONT_WEIGHT]; - if ([fontWeight isKindOfClass:NSNumber.class]) { - [data handleNumericWeight:parent weight:[fontWeight doubleValue]]; - } else { - NSString* weight = fontWeight; - NSInteger fw = RNSVGFontWeightFromString(weight); - if (fw != -1) { - data->absoluteFontWeight = AbsoluteFontWeight((RNSVGFontWeight)fw, parent); - data->fontWeight = nearestFontWeight(data->absoluteFontWeight); - } else if ([weight length] != 0) { - [data handleNumericWeight:parent weight:[weight doubleValue]]; - } else { - [data setInheritedWeight:parent]; - } - } ++ (instancetype)initWithNSDictionary:(NSDictionary *)font parent:(RNSVGFontData *)parent +{ + RNSVGFontData *data = [RNSVGFontData alloc]; + CGFloat parentFontSize = parent->fontSize; + if ([font objectForKey:FONT_SIZE]) { + id fontSize = [font objectForKey:FONT_SIZE]; + if ([fontSize isKindOfClass:NSNumber.class]) { + NSNumber *fs = fontSize; + data->fontSize = (CGFloat)[fs doubleValue]; } else { + data->fontSize = [RNSVGPropHelper fromRelativeWithNSString:fontSize + relative:parentFontSize + fontSize:parentFontSize]; + } + } else { + data->fontSize = parentFontSize; + } + + if ([font objectForKey:FONT_WEIGHT]) { + id fontWeight = [font objectForKey:FONT_WEIGHT]; + if ([fontWeight isKindOfClass:NSNumber.class]) { + [data handleNumericWeight:parent weight:[fontWeight doubleValue]]; + } else { + NSString *weight = fontWeight; + NSInteger fw = RNSVGFontWeightFromString(weight); + if (fw != -1) { + data->absoluteFontWeight = AbsoluteFontWeight((RNSVGFontWeight)fw, parent); + data->fontWeight = nearestFontWeight(data->absoluteFontWeight); + } else if ([weight length] != 0) { + [data handleNumericWeight:parent weight:[weight doubleValue]]; + } else { [data setInheritedWeight:parent]; + } } + } else { + [data setInheritedWeight:parent]; + } - data->fontData = [font objectForKey:FONT_DATA] ? [font objectForKey:FONT_DATA] : parent->fontData; - data->fontFamily = [font objectForKey:FONT_FAMILY] ? [font objectForKey:FONT_FAMILY] : parent->fontFamily; - NSString* style = [font objectForKey:FONT_STYLE]; - data->fontStyle = style ? RNSVGFontStyleFromString(style) : parent->fontStyle; - NSString* feature = [font objectForKey:FONT_FEATURE_SETTINGS]; - data->fontFeatureSettings = feature ? [font objectForKey:FONT_FEATURE_SETTINGS] : parent->fontFeatureSettings; - NSString* variant = [font objectForKey:FONT_VARIANT_LIGATURES]; - data->fontVariantLigatures = variant ? RNSVGFontVariantLigaturesFromString(variant) : parent->fontVariantLigatures; - NSString* anchor = [font objectForKey:TEXT_ANCHOR]; - data->textAnchor = anchor ? RNSVGTextAnchorFromString(anchor) : parent->textAnchor; - NSString* decoration = [font objectForKey:TEXT_DECORATION]; - data->textDecoration = decoration ? RNSVGTextDecorationFromString(decoration) : parent->textDecoration; + data->fontData = [font objectForKey:FONT_DATA] ? [font objectForKey:FONT_DATA] : parent->fontData; + data->fontFamily = [font objectForKey:FONT_FAMILY] ? [font objectForKey:FONT_FAMILY] : parent->fontFamily; + NSString *style = [font objectForKey:FONT_STYLE]; + data->fontStyle = style ? RNSVGFontStyleFromString(style) : parent->fontStyle; + NSString *feature = [font objectForKey:FONT_FEATURE_SETTINGS]; + data->fontFeatureSettings = feature ? [font objectForKey:FONT_FEATURE_SETTINGS] : parent->fontFeatureSettings; + NSString *variant = [font objectForKey:FONT_VARIANT_LIGATURES]; + data->fontVariantLigatures = variant ? RNSVGFontVariantLigaturesFromString(variant) : parent->fontVariantLigatures; + NSString *anchor = [font objectForKey:TEXT_ANCHOR]; + data->textAnchor = anchor ? RNSVGTextAnchorFromString(anchor) : parent->textAnchor; + NSString *decoration = [font objectForKey:TEXT_DECORATION]; + data->textDecoration = decoration ? RNSVGTextDecorationFromString(decoration) : parent->textDecoration; - CGFloat fontSize = data->fontSize; - id kerning = [font objectForKey:KERNING]; - data->manualKerning = (kerning || parent->manualKerning ); - if ([kerning isKindOfClass:NSNumber.class]) { - NSNumber* kern = kerning; - data->kerning = (CGFloat)[kern doubleValue]; - } else { - data->kerning = kerning ? - [RNSVGFontData toAbsoluteWithNSString:kerning - fontSize:fontSize] - : parent->kerning; - } + CGFloat fontSize = data->fontSize; + id kerning = [font objectForKey:KERNING]; + data->manualKerning = (kerning || parent->manualKerning); + if ([kerning isKindOfClass:NSNumber.class]) { + NSNumber *kern = kerning; + data->kerning = (CGFloat)[kern doubleValue]; + } else { + data->kerning = kerning ? [RNSVGFontData toAbsoluteWithNSString:kerning fontSize:fontSize] : parent->kerning; + } - id wordSpacing = [font objectForKey:WORD_SPACING]; - if ([wordSpacing isKindOfClass:NSNumber.class]) { - NSNumber* ws = wordSpacing; - data->wordSpacing = (CGFloat)[ws doubleValue]; - } else { - data->wordSpacing = wordSpacing ? - [RNSVGFontData toAbsoluteWithNSString:wordSpacing - fontSize:fontSize] - : parent->wordSpacing; - } + id wordSpacing = [font objectForKey:WORD_SPACING]; + if ([wordSpacing isKindOfClass:NSNumber.class]) { + NSNumber *ws = wordSpacing; + data->wordSpacing = (CGFloat)[ws doubleValue]; + } else { + data->wordSpacing = + wordSpacing ? [RNSVGFontData toAbsoluteWithNSString:wordSpacing fontSize:fontSize] : parent->wordSpacing; + } - id letterSpacing = [font objectForKey:LETTER_SPACING]; - if ([letterSpacing isKindOfClass:NSNumber.class]) { - NSNumber* ls = letterSpacing; - data->wordSpacing = (CGFloat)[ls doubleValue]; - } else { - data->letterSpacing = letterSpacing ? - [RNSVGFontData toAbsoluteWithNSString:letterSpacing - fontSize:fontSize] - : parent->letterSpacing; - } + id letterSpacing = [font objectForKey:LETTER_SPACING]; + if ([letterSpacing isKindOfClass:NSNumber.class]) { + NSNumber *ls = letterSpacing; + data->wordSpacing = (CGFloat)[ls doubleValue]; + } else { + data->letterSpacing = + letterSpacing ? [RNSVGFontData toAbsoluteWithNSString:letterSpacing fontSize:fontSize] : parent->letterSpacing; + } - return data; + return data; } - @end diff --git a/apple/Text/RNSVGGlyphContext.h b/apple/Text/RNSVGGlyphContext.h index de49110e..cccb0ccb 100644 --- a/apple/Text/RNSVGGlyphContext.h +++ b/apple/Text/RNSVGGlyphContext.h @@ -1,5 +1,5 @@ -#import #import +#import #import "RNSVGFontData.h" @class RNSVGText; @@ -10,8 +10,7 @@ - (CTFontRef)getGlyphFont; -- (instancetype)initWithWidth:(CGFloat)width - height:(CGFloat)height; +- (instancetype)initWithWidth:(CGFloat)width height:(CGFloat)height; - (RNSVGFontData *)getFont; @@ -33,17 +32,16 @@ - (void)popContext; -- (void)pushContext:(RNSVGText*)node - font:(NSDictionary*)font - x:(NSArray*)x - y:(NSArray*)y - deltaX:(NSArray*)deltaX - deltaY:(NSArray*)deltaY - rotate:(NSArray*)rotate; +- (void)pushContext:(RNSVGText *)node + font:(NSDictionary *)font + x:(NSArray *)x + y:(NSArray *)y + deltaX:(NSArray *)deltaX + deltaY:(NSArray *)deltaY + rotate:(NSArray *)rotate; -- (void)pushContext:(RNSVGGroup*)node - font:(NSDictionary *)font; +- (void)pushContext:(RNSVGGroup *)node font:(NSDictionary *)font; -- (NSArray*)getFontContext; +- (NSArray *)getFontContext; @end diff --git a/apple/Text/RNSVGGlyphContext.mm b/apple/Text/RNSVGGlyphContext.mm index 1e3b61ca..aec1058a 100644 --- a/apple/Text/RNSVGGlyphContext.mm +++ b/apple/Text/RNSVGGlyphContext.mm @@ -1,414 +1,414 @@ #import "RNSVGGlyphContext.h" #import +#import "RNSVGFontData.h" #import "RNSVGNode.h" #import "RNSVGPropHelper.h" -#import "RNSVGFontData.h" #import "RNSVGText.h" // https://www.w3.org/TR/SVG/text.html#TSpanElement @interface RNSVGGlyphContext () { -@public - // Current stack (one per node push/pop) - NSMutableArray *mFontContext_; + @public + // Current stack (one per node push/pop) + NSMutableArray *mFontContext_; - // Unique input attribute lists (only added if node sets a value) - NSMutableArray*> *mXsContext_; - NSMutableArray*> *mYsContext_; - NSMutableArray*> *mDXsContext_; - NSMutableArray*> *mDYsContext_; - NSMutableArray*> *mRsContext_; + // Unique input attribute lists (only added if node sets a value) + NSMutableArray *> *mXsContext_; + NSMutableArray *> *mYsContext_; + NSMutableArray *> *mDXsContext_; + NSMutableArray *> *mDYsContext_; + NSMutableArray *> *mRsContext_; - // Unique index into attribute list (one per unique list) - NSMutableArray *mXIndices_; - NSMutableArray *mYIndices_; - NSMutableArray *mDXIndices_; - NSMutableArray *mDYIndices_; - NSMutableArray *mRIndices_; + // Unique index into attribute list (one per unique list) + NSMutableArray *mXIndices_; + NSMutableArray *mYIndices_; + NSMutableArray *mDXIndices_; + NSMutableArray *mDYIndices_; + NSMutableArray *mRIndices_; - // Index of unique context used (one per node push/pop) - NSMutableArray *mXsIndices_; - NSMutableArray *mYsIndices_; - NSMutableArray *mDXsIndices_; - NSMutableArray *mDYsIndices_; - NSMutableArray *mRsIndices_; + // Index of unique context used (one per node push/pop) + NSMutableArray *mXsIndices_; + NSMutableArray *mYsIndices_; + NSMutableArray *mDXsIndices_; + NSMutableArray *mDYsIndices_; + NSMutableArray *mRsIndices_; - // Calculated on push context, percentage and em length depends on parent font size - CGFloat mFontSize_; - RNSVGFontData *topFont_; + // Calculated on push context, percentage and em length depends on parent font size + CGFloat mFontSize_; + RNSVGFontData *topFont_; - // Current accumulated values - // https://www.w3.org/TR/SVG/types.html#DataTypeCoordinate - // syntax is the same as that for - CGFloat mX_; - CGFloat mY_; + // Current accumulated values + // https://www.w3.org/TR/SVG/types.html#DataTypeCoordinate + // syntax is the same as that for + CGFloat mX_; + CGFloat mY_; - // https://www.w3.org/TR/SVG/types.html#Length - CGFloat mDX_; - CGFloat mDY_; + // https://www.w3.org/TR/SVG/types.html#Length + CGFloat mDX_; + CGFloat mDY_; - // Current SVGLengthList - // https://www.w3.org/TR/SVG/types.html#InterfaceSVGLengthList - // https://www.w3.org/TR/SVG/types.html#DataTypeCoordinates + // Current SVGLengthList + // https://www.w3.org/TR/SVG/types.html#InterfaceSVGLengthList + // https://www.w3.org/TR/SVG/types.html#DataTypeCoordinates - // https://www.w3.org/TR/SVG/text.html#TSpanElementXAttribute - NSArray *mXs_; + // https://www.w3.org/TR/SVG/text.html#TSpanElementXAttribute + NSArray *mXs_; - // https://www.w3.org/TR/SVG/text.html#TSpanElementYAttribute - NSArray *mYs_; + // https://www.w3.org/TR/SVG/text.html#TSpanElementYAttribute + NSArray *mYs_; - // Current SVGLengthList - // https://www.w3.org/TR/SVG/types.html#DataTypeLengths + // Current SVGLengthList + // https://www.w3.org/TR/SVG/types.html#DataTypeLengths - // https://www.w3.org/TR/SVG/text.html#TSpanElementDXAttribute - NSArray *mDXs_; + // https://www.w3.org/TR/SVG/text.html#TSpanElementDXAttribute + NSArray *mDXs_; - // https://www.w3.org/TR/SVG/text.html#TSpanElementDYAttribute - NSArray *mDYs_; + // https://www.w3.org/TR/SVG/text.html#TSpanElementDYAttribute + NSArray *mDYs_; - // Current SVGLengthList - // https://www.w3.org/TR/SVG/types.html#DataTypeNumbers + // Current SVGLengthList + // https://www.w3.org/TR/SVG/types.html#DataTypeNumbers - // https://www.w3.org/TR/SVG/text.html#TSpanElementRotateAttribute - NSArray *mRs_; + // https://www.w3.org/TR/SVG/text.html#TSpanElementRotateAttribute + NSArray *mRs_; - // Current attribute list index - long mXsIndex_; - long mYsIndex_; - long mDXsIndex_; - long mDYsIndex_; - long mRsIndex_; + // Current attribute list index + long mXsIndex_; + long mYsIndex_; + long mDXsIndex_; + long mDYsIndex_; + long mRsIndex_; - // Current value index in current attribute list - long mXIndex_; - long mYIndex_; - long mDXIndex_; - long mDYIndex_; - long mRIndex_; + // Current value index in current attribute list + long mXIndex_; + long mYIndex_; + long mDXIndex_; + long mDYIndex_; + long mRIndex_; - // Top index of stack - long mTop_; + // Top index of stack + long mTop_; - // Constructor parameters - CGFloat mWidth_; - CGFloat mHeight_; + // Constructor parameters + CGFloat mWidth_; + CGFloat mHeight_; } -- (void)pushContext:(RNSVGText*)node - font:(NSDictionary*)font - x:(NSArray*)x - y:(NSArray*)y - deltaX:(NSArray*)deltaX - deltaY:(NSArray*)deltaY - rotate:(NSArray*)rotate; +- (void)pushContext:(RNSVGText *)node + font:(NSDictionary *)font + x:(NSArray *)x + y:(NSArray *)y + deltaX:(NSArray *)deltaX + deltaY:(NSArray *)deltaY + rotate:(NSArray *)rotate; -- (void)pushContext:(RNSVGGroup*)node - font:(NSDictionary *)font; +- (void)pushContext:(RNSVGGroup *)node font:(NSDictionary *)font; @end @implementation RNSVGGlyphContext -- (NSArray*)getFontContext { - return mFontContext_; +- (NSArray *)getFontContext +{ + return mFontContext_; } - (CTFontRef)getGlyphFont { - CGFloat size = topFont_->fontSize; - NSString *fontFamily = topFont_->fontFamily; - NSString *fontStyle = RNSVGFontStyleStrings[topFont_->fontStyle]; - NSString *fontWeight = RNSVGFontWeightStrings[topFont_->fontWeight]; - UIFont *font = [RCTFont updateFont:nil - withFamily:[fontFamily isEqualToString:@""] ? nil : fontFamily - size:@(isnan(size) ? 0 : size) - weight:fontWeight - style:fontStyle - variant:nil - scaleMultiplier:1.0]; - CTFontRef ref = (__bridge CTFontRef)font; + CGFloat size = topFont_->fontSize; + NSString *fontFamily = topFont_->fontFamily; + NSString *fontStyle = RNSVGFontStyleStrings[topFont_->fontStyle]; + NSString *fontWeight = RNSVGFontWeightStrings[topFont_->fontWeight]; + UIFont *font = [RCTFont updateFont:nil + withFamily:[fontFamily isEqualToString:@""] ? nil : fontFamily + size:@(isnan(size) ? 0 : size) + weight:fontWeight + style:fontStyle + variant:nil + scaleMultiplier:1.0]; + CTFontRef ref = (__bridge CTFontRef)font; - double weight = topFont_->absoluteFontWeight; - if (weight == 400) { - return ref; + double weight = topFont_->absoluteFontWeight; + if (weight == 400) { + return ref; + } + + CFArrayRef cgAxes = CTFontCopyVariationAxes(ref); + if (cgAxes == 0) { + return ref; + } + CFIndex cgAxisCount = CFArrayGetCount(cgAxes); + CFNumberRef wght_id = 0; + + for (CFIndex i = 0; i < cgAxisCount; ++i) { + CFTypeRef cgAxis = CFArrayGetValueAtIndex(cgAxes, i); + if (CFGetTypeID(cgAxis) != CFDictionaryGetTypeID()) { + continue; } - CFArrayRef cgAxes = CTFontCopyVariationAxes(ref); - if (cgAxes == 0) { - return ref; + CFDictionaryRef cgAxisDict = (CFDictionaryRef)cgAxis; + CFTypeRef axisName = CFDictionaryGetValue(cgAxisDict, kCTFontVariationAxisNameKey); + + if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) { + continue; } - CFIndex cgAxisCount = CFArrayGetCount(cgAxes); - CFNumberRef wght_id = 0; - - for (CFIndex i = 0; i < cgAxisCount; ++i) { - CFTypeRef cgAxis = CFArrayGetValueAtIndex(cgAxes, i); - if (CFGetTypeID(cgAxis) != CFDictionaryGetTypeID()) { - continue; - } - - CFDictionaryRef cgAxisDict = (CFDictionaryRef)cgAxis; - CFTypeRef axisName = CFDictionaryGetValue(cgAxisDict, kCTFontVariationAxisNameKey); - - if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) { - continue; - } - CFStringRef axisNameString = (CFStringRef)axisName; - NSString *axisNameNSString = (__bridge NSString *)(axisNameString); - if (![@"Weight" isEqualToString:axisNameNSString] && ![@"Size" isEqualToString:axisNameNSString]) { - continue; - } - - CFTypeRef axisMinValue = CFDictionaryGetValue(cgAxisDict, kCTFontVariationAxisMinimumValueKey); - if (axisMinValue && CFGetTypeID(axisMinValue) == CFNumberGetTypeID()) { - CFNumberRef axisMinValueNumber = (CFNumberRef)axisMinValue; - double axisMinValueDouble; - if (CFNumberGetValue(axisMinValueNumber, kCFNumberDoubleType, &axisMinValueDouble)) - { - weight = fmax(axisMinValueDouble, weight); - } - } - - CFTypeRef axisMaxValue = CFDictionaryGetValue(cgAxisDict, kCTFontVariationAxisMaximumValueKey); - if (axisMaxValue && CFGetTypeID(axisMaxValue) == CFNumberGetTypeID()) { - CFNumberRef axisMaxValueNumber = (CFNumberRef)axisMaxValue; - double axisMaxValueDouble; - if (CFNumberGetValue(axisMaxValueNumber, kCFNumberDoubleType, &axisMaxValueDouble)) - { - weight = fmin(axisMaxValueDouble, weight); - } - } - - CFTypeRef axisId = CFDictionaryGetValue(cgAxisDict, kCTFontVariationAxisIdentifierKey); - if (!axisId || CFGetTypeID(axisId) != CFNumberGetTypeID()) { - continue; - } - wght_id = (CFNumberRef)axisId; - break; + CFStringRef axisNameString = (CFStringRef)axisName; + NSString *axisNameNSString = (__bridge NSString *)(axisNameString); + if (![@"Weight" isEqualToString:axisNameNSString] && ![@"Size" isEqualToString:axisNameNSString]) { + continue; } - if (wght_id == 0) { - return ref; + CFTypeRef axisMinValue = CFDictionaryGetValue(cgAxisDict, kCTFontVariationAxisMinimumValueKey); + if (axisMinValue && CFGetTypeID(axisMinValue) == CFNumberGetTypeID()) { + CFNumberRef axisMinValueNumber = (CFNumberRef)axisMinValue; + double axisMinValueDouble; + if (CFNumberGetValue(axisMinValueNumber, kCFNumberDoubleType, &axisMinValueDouble)) { + weight = fmax(axisMinValueDouble, weight); + } } - UIFontDescriptor *uifd = font.fontDescriptor; - CTFontDescriptorRef ctfd = (__bridge CTFontDescriptorRef)(uifd); - CTFontDescriptorRef newfd = CTFontDescriptorCreateCopyWithVariation(ctfd, wght_id, (CGFloat)weight); - CTFontRef newfont = CTFontCreateCopyWithAttributes(ref, size, nil, newfd); - CFRelease(newfd); + CFTypeRef axisMaxValue = CFDictionaryGetValue(cgAxisDict, kCTFontVariationAxisMaximumValueKey); + if (axisMaxValue && CFGetTypeID(axisMaxValue) == CFNumberGetTypeID()) { + CFNumberRef axisMaxValueNumber = (CFNumberRef)axisMaxValue; + double axisMaxValueDouble; + if (CFNumberGetValue(axisMaxValueNumber, kCFNumberDoubleType, &axisMaxValueDouble)) { + weight = fmin(axisMaxValueDouble, weight); + } + } - return (CTFontRef)CFAutorelease(newfont); + CFTypeRef axisId = CFDictionaryGetValue(cgAxisDict, kCTFontVariationAxisIdentifierKey); + if (!axisId || CFGetTypeID(axisId) != CFNumberGetTypeID()) { + continue; + } + wght_id = (CFNumberRef)axisId; + break; + } + + if (wght_id == 0) { + return ref; + } + UIFontDescriptor *uifd = font.fontDescriptor; + CTFontDescriptorRef ctfd = (__bridge CTFontDescriptorRef)(uifd); + CTFontDescriptorRef newfd = CTFontDescriptorCreateCopyWithVariation(ctfd, wght_id, (CGFloat)weight); + CTFontRef newfont = CTFontCreateCopyWithAttributes(ref, size, nil, newfd); + + CFRelease(newfd); + + return (CTFontRef)CFAutorelease(newfont); } - (void)pushIndices { - [self->mXsIndices_ addObject:[NSNumber numberWithLong:self->mXsIndex_]]; - [self->mYsIndices_ addObject:[NSNumber numberWithLong:self->mYsIndex_]]; - [self->mDXsIndices_ addObject:[NSNumber numberWithLong:self->mDXsIndex_]]; - [self->mDYsIndices_ addObject:[NSNumber numberWithLong:self->mDYsIndex_]]; - [self->mRsIndices_ addObject:[NSNumber numberWithLong:self->mRsIndex_]]; + [self->mXsIndices_ addObject:[NSNumber numberWithLong:self->mXsIndex_]]; + [self->mYsIndices_ addObject:[NSNumber numberWithLong:self->mYsIndex_]]; + [self->mDXsIndices_ addObject:[NSNumber numberWithLong:self->mDXsIndex_]]; + [self->mDYsIndices_ addObject:[NSNumber numberWithLong:self->mDYsIndex_]]; + [self->mRsIndices_ addObject:[NSNumber numberWithLong:self->mRsIndex_]]; } -- (instancetype)initWithWidth:(CGFloat)width - height:(CGFloat)height { - self = [super init]; - self->mFontContext_ = [[NSMutableArray alloc]init]; - self->mXsContext_ = [[NSMutableArray alloc]init]; - self->mYsContext_ = [[NSMutableArray alloc]init]; - self->mDXsContext_ = [[NSMutableArray alloc]init]; - self->mDYsContext_ = [[NSMutableArray alloc]init]; - self->mRsContext_ = [[NSMutableArray alloc]init]; +- (instancetype)initWithWidth:(CGFloat)width height:(CGFloat)height +{ + self = [super init]; + self->mFontContext_ = [[NSMutableArray alloc] init]; + self->mXsContext_ = [[NSMutableArray alloc] init]; + self->mYsContext_ = [[NSMutableArray alloc] init]; + self->mDXsContext_ = [[NSMutableArray alloc] init]; + self->mDYsContext_ = [[NSMutableArray alloc] init]; + self->mRsContext_ = [[NSMutableArray alloc] init]; - self->mXIndices_ = [[NSMutableArray alloc]init]; - self->mYIndices_ = [[NSMutableArray alloc]init]; - self->mDXIndices_ = [[NSMutableArray alloc]init]; - self->mDYIndices_ = [[NSMutableArray alloc]init]; - self->mRIndices_ = [[NSMutableArray alloc]init]; + self->mXIndices_ = [[NSMutableArray alloc] init]; + self->mYIndices_ = [[NSMutableArray alloc] init]; + self->mDXIndices_ = [[NSMutableArray alloc] init]; + self->mDYIndices_ = [[NSMutableArray alloc] init]; + self->mRIndices_ = [[NSMutableArray alloc] init]; - self->mXsIndices_ = [[NSMutableArray alloc]init]; - self->mYsIndices_ = [[NSMutableArray alloc]init]; - self->mDXsIndices_ = [[NSMutableArray alloc]init]; - self->mDYsIndices_ = [[NSMutableArray alloc]init]; - self->mRsIndices_ = [[NSMutableArray alloc]init]; + self->mXsIndices_ = [[NSMutableArray alloc] init]; + self->mYsIndices_ = [[NSMutableArray alloc] init]; + self->mDXsIndices_ = [[NSMutableArray alloc] init]; + self->mDYsIndices_ = [[NSMutableArray alloc] init]; + self->mRsIndices_ = [[NSMutableArray alloc] init]; - self->mFontSize_ = RNSVGFontData_DEFAULT_FONT_SIZE; - self->topFont_ = [RNSVGFontData Defaults]; + self->mFontSize_ = RNSVGFontData_DEFAULT_FONT_SIZE; + self->topFont_ = [RNSVGFontData Defaults]; - self->mXs_ = [[NSArray alloc]init]; - self->mYs_ = [[NSArray alloc]init]; - self->mDXs_ = [[NSArray alloc]init]; - self->mDYs_ = [[NSArray alloc]init]; - self->mRs_ = [[NSArray alloc]initWithObjects:[RNSVGLength lengthWithNumber:0], nil]; + self->mXs_ = [[NSArray alloc] init]; + self->mYs_ = [[NSArray alloc] init]; + self->mDXs_ = [[NSArray alloc] init]; + self->mDYs_ = [[NSArray alloc] init]; + self->mRs_ = [[NSArray alloc] initWithObjects:[RNSVGLength lengthWithNumber:0], nil]; - self->mXIndex_ = -1; - self->mYIndex_ = -1; - self->mDXIndex_ = -1; - self->mDYIndex_ = -1; - self->mRIndex_ = -1; + self->mXIndex_ = -1; + self->mYIndex_ = -1; + self->mDXIndex_ = -1; + self->mDYIndex_ = -1; + self->mRIndex_ = -1; - self->mWidth_ = width; - self->mHeight_ = height; + self->mWidth_ = width; + self->mHeight_ = height; - [self->mXsContext_ addObject:self->mXs_]; - [self->mYsContext_ addObject:self->mYs_]; - [self->mDXsContext_ addObject:self->mDXs_]; - [self->mDYsContext_ addObject:self->mDYs_]; - [self->mRsContext_ addObject:self->mRs_]; + [self->mXsContext_ addObject:self->mXs_]; + [self->mYsContext_ addObject:self->mYs_]; + [self->mDXsContext_ addObject:self->mDXs_]; + [self->mDYsContext_ addObject:self->mDYs_]; + [self->mRsContext_ addObject:self->mRs_]; - [self->mXIndices_ addObject:[NSNumber numberWithLong:self->mXIndex_]]; - [self->mYIndices_ addObject:[NSNumber numberWithLong:self->mYIndex_]]; - [self->mDXIndices_ addObject:[NSNumber numberWithLong:self->mDXIndex_]]; - [self->mDYIndices_ addObject:[NSNumber numberWithLong:self->mDYIndex_]]; - [self->mRIndices_ addObject:[NSNumber numberWithLong:self->mRIndex_]]; + [self->mXIndices_ addObject:[NSNumber numberWithLong:self->mXIndex_]]; + [self->mYIndices_ addObject:[NSNumber numberWithLong:self->mYIndex_]]; + [self->mDXIndices_ addObject:[NSNumber numberWithLong:self->mDXIndex_]]; + [self->mDYIndices_ addObject:[NSNumber numberWithLong:self->mDYIndex_]]; + [self->mRIndices_ addObject:[NSNumber numberWithLong:self->mRIndex_]]; - [self->mFontContext_ addObject:self->topFont_]; - [self pushIndices]; - return self; + [self->mFontContext_ addObject:self->topFont_]; + [self pushIndices]; + return self; } -- (RNSVGFontData *)getFont { - return topFont_; +- (RNSVGFontData *)getFont +{ + return topFont_; } - (RNSVGFontData *)getTopOrParentFont:(RNSVGGroup *)child { - if (self->mTop_ > 0) { - return self->topFont_; - } else { - RNSVGGroup *parentRoot = [child getParentTextRoot]; - RNSVGFontData *Defaults = [RNSVGFontData Defaults]; - while (parentRoot != nil) { - RNSVGFontData *map = [[parentRoot getGlyphContext] getFont]; - if (map != Defaults) { - return map; - } - parentRoot = [parentRoot getParentTextRoot]; - } - return Defaults; + if (self->mTop_ > 0) { + return self->topFont_; + } else { + RNSVGGroup *parentRoot = [child getParentTextRoot]; + RNSVGFontData *Defaults = [RNSVGFontData Defaults]; + while (parentRoot != nil) { + RNSVGFontData *map = [[parentRoot getGlyphContext] getFont]; + if (map != Defaults) { + return map; + } + parentRoot = [parentRoot getParentTextRoot]; } + return Defaults; + } } - (void)pushNode:(RNSVGGroup *)node andFont:(NSDictionary *)font { - RNSVGFontData *parent = [self getTopOrParentFont:node]; - self->mTop_++; - if (font == nil) { - [self->mFontContext_ addObject:parent]; - return; - } - RNSVGFontData *data = [RNSVGFontData initWithNSDictionary:font - parent:parent]; - self->mFontSize_ = data->fontSize; - [self->mFontContext_ addObject:data]; - self->topFont_ = data; + RNSVGFontData *parent = [self getTopOrParentFont:node]; + self->mTop_++; + if (font == nil) { + [self->mFontContext_ addObject:parent]; + return; + } + RNSVGFontData *data = [RNSVGFontData initWithNSDictionary:font parent:parent]; + self->mFontSize_ = data->fontSize; + [self->mFontContext_ addObject:data]; + self->topFont_ = data; } -- (void)pushContext:(RNSVGGroup*)node - font:(NSDictionary*)font { - [self pushNode:node andFont:font]; - [self pushIndices]; +- (void)pushContext:(RNSVGGroup *)node font:(NSDictionary *)font +{ + [self pushNode:node andFont:font]; + [self pushIndices]; } -- (void)pushContext:(RNSVGText*)node - font:(NSDictionary*)font - x:(NSArray*)x - y:(NSArray*)y - deltaX:(NSArray*)deltaX - deltaY:(NSArray*)deltaY - rotate:(NSArray*)rotate { - [self pushNode:(RNSVGGroup*)node andFont:font]; - if (x != nil && [x count] != 0) { - mXsIndex_++; - mXIndex_ = -1; - [mXIndices_ addObject:[NSNumber numberWithLong:mXIndex_]]; - mXs_ = x; - [mXsContext_ addObject:mXs_]; - } - if (y != nil && [y count] != 0) { - mYsIndex_++; - mYIndex_ = -1; - [mYIndices_ addObject:[NSNumber numberWithLong:mYIndex_]]; - mYs_ = y; - [mYsContext_ addObject:mYs_]; - } - if (deltaX != nil && [deltaX count] != 0) { - mDXsIndex_++; - mDXIndex_ = -1; - [mDXIndices_ addObject:[NSNumber numberWithLong:mDXIndex_]]; - mDXs_ = deltaX; - [mDXsContext_ addObject:mDXs_]; - } - if (deltaY != nil && [deltaY count] != 0) { - mDYsIndex_++; - mDYIndex_ = -1; - [mDYIndices_ addObject:[NSNumber numberWithLong:mDYIndex_]]; - mDYs_ = deltaY; - [mDYsContext_ addObject:mDYs_]; - } - if (rotate != nil && [rotate count] != 0) { - mRsIndex_++; - mRIndex_ = -1; - [mRIndices_ addObject:[NSNumber numberWithLong:mRIndex_]]; - mRs_ = rotate; - [mRsContext_ addObject:mRs_]; - } - [self pushIndices]; +- (void)pushContext:(RNSVGText *)node + font:(NSDictionary *)font + x:(NSArray *)x + y:(NSArray *)y + deltaX:(NSArray *)deltaX + deltaY:(NSArray *)deltaY + rotate:(NSArray *)rotate +{ + [self pushNode:(RNSVGGroup *)node andFont:font]; + if (x != nil && [x count] != 0) { + mXsIndex_++; + mXIndex_ = -1; + [mXIndices_ addObject:[NSNumber numberWithLong:mXIndex_]]; + mXs_ = x; + [mXsContext_ addObject:mXs_]; + } + if (y != nil && [y count] != 0) { + mYsIndex_++; + mYIndex_ = -1; + [mYIndices_ addObject:[NSNumber numberWithLong:mYIndex_]]; + mYs_ = y; + [mYsContext_ addObject:mYs_]; + } + if (deltaX != nil && [deltaX count] != 0) { + mDXsIndex_++; + mDXIndex_ = -1; + [mDXIndices_ addObject:[NSNumber numberWithLong:mDXIndex_]]; + mDXs_ = deltaX; + [mDXsContext_ addObject:mDXs_]; + } + if (deltaY != nil && [deltaY count] != 0) { + mDYsIndex_++; + mDYIndex_ = -1; + [mDYIndices_ addObject:[NSNumber numberWithLong:mDYIndex_]]; + mDYs_ = deltaY; + [mDYsContext_ addObject:mDYs_]; + } + if (rotate != nil && [rotate count] != 0) { + mRsIndex_++; + mRIndex_ = -1; + [mRIndices_ addObject:[NSNumber numberWithLong:mRIndex_]]; + mRs_ = rotate; + [mRsContext_ addObject:mRs_]; + } + [self pushIndices]; } -- (void)popContext { - [mFontContext_ removeLastObject]; - [mXsIndices_ removeLastObject]; - [mYsIndices_ removeLastObject]; - [mDXsIndices_ removeLastObject]; - [mDYsIndices_ removeLastObject]; - [mRsIndices_ removeLastObject]; +- (void)popContext +{ + [mFontContext_ removeLastObject]; + [mXsIndices_ removeLastObject]; + [mYsIndices_ removeLastObject]; + [mDXsIndices_ removeLastObject]; + [mDYsIndices_ removeLastObject]; + [mRsIndices_ removeLastObject]; - mTop_--; + mTop_--; - long x = mXsIndex_; - long y = mYsIndex_; - long dx = mDXsIndex_; - long dy = mDYsIndex_; - long r = mRsIndex_; + long x = mXsIndex_; + long y = mYsIndex_; + long dx = mDXsIndex_; + long dy = mDYsIndex_; + long r = mRsIndex_; - topFont_ = [mFontContext_ lastObject]; + topFont_ = [mFontContext_ lastObject]; - mXsIndex_ = [[mXsIndices_ lastObject] longValue]; - mYsIndex_ = [[mYsIndices_ lastObject] longValue]; - mDXsIndex_ = [[mDXsIndices_ lastObject] longValue]; - mDYsIndex_ = [[mDYsIndices_ lastObject] longValue]; - mRsIndex_ = [[mRsIndices_ lastObject] longValue]; + mXsIndex_ = [[mXsIndices_ lastObject] longValue]; + mYsIndex_ = [[mYsIndices_ lastObject] longValue]; + mDXsIndex_ = [[mDXsIndices_ lastObject] longValue]; + mDYsIndex_ = [[mDYsIndices_ lastObject] longValue]; + mRsIndex_ = [[mRsIndices_ lastObject] longValue]; - if (x != mXsIndex_) { - [mXsContext_ removeObjectAtIndex:x]; - mXs_ = [mXsContext_ objectAtIndex:mXsIndex_]; - mXIndex_ = [[mXIndices_ objectAtIndex:mXsIndex_] longValue]; - } - if (y != mYsIndex_) { - [mYsContext_ removeObjectAtIndex:y]; - mYs_ = [mYsContext_ objectAtIndex:mYsIndex_]; - mYIndex_ = [[mYIndices_ objectAtIndex:mYsIndex_] longValue]; - } - if (dx != mDXsIndex_) { - [mDXsContext_ removeObjectAtIndex:dx]; - mDXs_ = [mDXsContext_ objectAtIndex:mDXsIndex_]; - mDXIndex_ = [[mDXIndices_ objectAtIndex:mDXsIndex_] longValue]; - } - if (dy != mDYsIndex_) { - [mDYsContext_ removeObjectAtIndex:dy]; - mDYs_ = [mDYsContext_ objectAtIndex:mDYsIndex_]; - mDYIndex_ = [[mDYIndices_ objectAtIndex:mDYsIndex_] longValue]; - } - if (r != mRsIndex_) { - [mRsContext_ removeObjectAtIndex:r]; - mRs_ = [mRsContext_ objectAtIndex:mRsIndex_]; - mRIndex_ = [[mRIndices_ objectAtIndex:mRsIndex_] longValue]; - } + if (x != mXsIndex_) { + [mXsContext_ removeObjectAtIndex:x]; + mXs_ = [mXsContext_ objectAtIndex:mXsIndex_]; + mXIndex_ = [[mXIndices_ objectAtIndex:mXsIndex_] longValue]; + } + if (y != mYsIndex_) { + [mYsContext_ removeObjectAtIndex:y]; + mYs_ = [mYsContext_ objectAtIndex:mYsIndex_]; + mYIndex_ = [[mYIndices_ objectAtIndex:mYsIndex_] longValue]; + } + if (dx != mDXsIndex_) { + [mDXsContext_ removeObjectAtIndex:dx]; + mDXs_ = [mDXsContext_ objectAtIndex:mDXsIndex_]; + mDXIndex_ = [[mDXIndices_ objectAtIndex:mDXsIndex_] longValue]; + } + if (dy != mDYsIndex_) { + [mDYsContext_ removeObjectAtIndex:dy]; + mDYs_ = [mDYsContext_ objectAtIndex:mDYsIndex_]; + mDYIndex_ = [[mDYIndices_ objectAtIndex:mDYsIndex_] longValue]; + } + if (r != mRsIndex_) { + [mRsContext_ removeObjectAtIndex:r]; + mRs_ = [mRsContext_ objectAtIndex:mRsIndex_]; + mRIndex_ = [[mRIndices_ objectAtIndex:mRsIndex_] longValue]; + } } + (void)incrementIndices:(NSMutableArray *)indices topIndex:(long)topIndex { - for (long index = topIndex; index >= 0; index--) { - long xIndex = [[indices objectAtIndex:index] longValue]; - [indices setObject:[NSNumber numberWithLong:xIndex + 1] atIndexedSubscript:index]; - } + for (long index = topIndex; index >= 0; index--) { + long xIndex = [[indices objectAtIndex:index] longValue]; + [indices setObject:[NSNumber numberWithLong:xIndex + 1] atIndexedSubscript:index]; + } } // https://www.w3.org/TR/SVG11/text.html#FontSizeProperty @@ -442,84 +442,84 @@ * Except for any additional information provided in this specification, * the normative definition of the property is in CSS2 ([CSS2], section 15.2.4). */ -- (CGFloat)getFontSize { - return mFontSize_; +- (CGFloat)getFontSize +{ + return mFontSize_; } -- (CGFloat)nextXWithDouble:(CGFloat)advance { - [RNSVGGlyphContext incrementIndices:mXIndices_ topIndex:mXsIndex_]; - long nextIndex = mXIndex_ + 1; - if (nextIndex < [mXs_ count]) { - mDX_ = 0; - mXIndex_ = nextIndex; - RNSVGLength *length = [mXs_ objectAtIndex:nextIndex]; - mX_ = [RNSVGPropHelper fromRelative:length - relative:mWidth_ - fontSize:mFontSize_]; - } - mX_ += advance; - return mX_; +- (CGFloat)nextXWithDouble:(CGFloat)advance +{ + [RNSVGGlyphContext incrementIndices:mXIndices_ topIndex:mXsIndex_]; + long nextIndex = mXIndex_ + 1; + if (nextIndex < [mXs_ count]) { + mDX_ = 0; + mXIndex_ = nextIndex; + RNSVGLength *length = [mXs_ objectAtIndex:nextIndex]; + mX_ = [RNSVGPropHelper fromRelative:length relative:mWidth_ fontSize:mFontSize_]; + } + mX_ += advance; + return mX_; } -- (CGFloat)nextY { - [RNSVGGlyphContext incrementIndices:mYIndices_ topIndex:mYsIndex_]; - long nextIndex = mYIndex_ + 1; - if (nextIndex < [mYs_ count]) { - mDY_ = 0; - mYIndex_ = nextIndex; - RNSVGLength *length = [mYs_ objectAtIndex:nextIndex]; - mY_ = [RNSVGPropHelper fromRelative:length - relative:mHeight_ - fontSize:mFontSize_]; - } - return mY_; +- (CGFloat)nextY +{ + [RNSVGGlyphContext incrementIndices:mYIndices_ topIndex:mYsIndex_]; + long nextIndex = mYIndex_ + 1; + if (nextIndex < [mYs_ count]) { + mDY_ = 0; + mYIndex_ = nextIndex; + RNSVGLength *length = [mYs_ objectAtIndex:nextIndex]; + mY_ = [RNSVGPropHelper fromRelative:length relative:mHeight_ fontSize:mFontSize_]; + } + return mY_; } -- (CGFloat)nextDeltaX { - [RNSVGGlyphContext incrementIndices:mDXIndices_ topIndex:mDXsIndex_]; - long nextIndex = mDXIndex_ + 1; - if (nextIndex < [mDXs_ count]) { - mDXIndex_ = nextIndex; - RNSVGLength *length = [mDXs_ objectAtIndex:nextIndex]; - CGFloat val = [RNSVGPropHelper fromRelative:length - relative:mWidth_ - fontSize:mFontSize_]; - mDX_ += val; - } - return mDX_; +- (CGFloat)nextDeltaX +{ + [RNSVGGlyphContext incrementIndices:mDXIndices_ topIndex:mDXsIndex_]; + long nextIndex = mDXIndex_ + 1; + if (nextIndex < [mDXs_ count]) { + mDXIndex_ = nextIndex; + RNSVGLength *length = [mDXs_ objectAtIndex:nextIndex]; + CGFloat val = [RNSVGPropHelper fromRelative:length relative:mWidth_ fontSize:mFontSize_]; + mDX_ += val; + } + return mDX_; } -- (CGFloat)nextDeltaY { - [RNSVGGlyphContext incrementIndices:mDYIndices_ topIndex:mDYsIndex_]; - long nextIndex = mDYIndex_ + 1; - if (nextIndex < [mDYs_ count]) { - mDYIndex_ = nextIndex; - RNSVGLength *length = [mDYs_ objectAtIndex:nextIndex]; - CGFloat val = [RNSVGPropHelper fromRelative:length - relative:mHeight_ - fontSize:mFontSize_]; - mDY_ += val; - } - return mDY_; +- (CGFloat)nextDeltaY +{ + [RNSVGGlyphContext incrementIndices:mDYIndices_ topIndex:mDYsIndex_]; + long nextIndex = mDYIndex_ + 1; + if (nextIndex < [mDYs_ count]) { + mDYIndex_ = nextIndex; + RNSVGLength *length = [mDYs_ objectAtIndex:nextIndex]; + CGFloat val = [RNSVGPropHelper fromRelative:length relative:mHeight_ fontSize:mFontSize_]; + mDY_ += val; + } + return mDY_; } -- (CGFloat)nextRotation { - [RNSVGGlyphContext incrementIndices:mRIndices_ topIndex:mRsIndex_]; - long nextIndex = mRIndex_ + 1; - long count = [mRs_ count]; - if (nextIndex < count) { - mRIndex_ = nextIndex; - } else { - mRIndex_ = count - 1; - } - return [mRs_[mRIndex_] value]; +- (CGFloat)nextRotation +{ + [RNSVGGlyphContext incrementIndices:mRIndices_ topIndex:mRsIndex_]; + long nextIndex = mRIndex_ + 1; + long count = [mRs_ count]; + if (nextIndex < count) { + mRIndex_ = nextIndex; + } else { + mRIndex_ = count - 1; + } + return [mRs_[mRIndex_] value]; } -- (CGFloat)getWidth { - return mWidth_; +- (CGFloat)getWidth +{ + return mWidth_; } -- (CGFloat)getHeight { - return mHeight_; +- (CGFloat)getHeight +{ + return mHeight_; } @end diff --git a/apple/Text/RNSVGPropHelper.h b/apple/Text/RNSVGPropHelper.h index ce89239e..b33562f7 100644 --- a/apple/Text/RNSVGPropHelper.h +++ b/apple/Text/RNSVGPropHelper.h @@ -7,16 +7,11 @@ @interface RNSVGPropHelper : NSObject -+ (CGFloat) fromRelativeWithNSString:(NSString *)length - relative:(CGFloat)relative - fontSize:(CGFloat)fontSize; ++ (CGFloat)fromRelativeWithNSString:(NSString *)length relative:(CGFloat)relative fontSize:(CGFloat)fontSize; -+ (CGFloat) fromRelative:(RNSVGLength*)length - relative:(CGFloat)relative - fontSize:(CGFloat)fontSize; ++ (CGFloat)fromRelative:(RNSVGLength *)length relative:(CGFloat)relative fontSize:(CGFloat)fontSize; -+ (CGFloat)fromRelative:(RNSVGLength*)length - relative:(CGFloat)relative; ++ (CGFloat)fromRelative:(RNSVGLength *)length relative:(CGFloat)relative; @end #endif diff --git a/apple/Text/RNSVGPropHelper.mm b/apple/Text/RNSVGPropHelper.mm index 51470b1d..6e7fa076 100644 --- a/apple/Text/RNSVGPropHelper.mm +++ b/apple/Text/RNSVGPropHelper.mm @@ -1,138 +1,133 @@ #include "RNSVGPropHelper.h" @implementation RNSVGPropHelper -+ (CGFloat)fromRelativeWithNSString:(NSString *)length - relative:(CGFloat)relative - fontSize:(CGFloat)fontSize { - length = [length stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - NSUInteger stringLength = [length length]; - NSInteger percentIndex = stringLength - 1; - if (stringLength == 0) { - return 0; - } - else if ([length characterAtIndex:percentIndex] == '%') { - return (CGFloat)[[length substringWithRange:NSMakeRange(0, percentIndex)] doubleValue] / 100 * relative; - } - else { - NSInteger twoLetterUnitIndex = stringLength - 2; - if (twoLetterUnitIndex > 0) { - NSString *lastTwo = [length substringFromIndex:twoLetterUnitIndex]; - NSUInteger end = twoLetterUnitIndex; - CGFloat unit = 1; - if ([lastTwo isEqualToString:@"px"]) { ++ (CGFloat)fromRelativeWithNSString:(NSString *)length relative:(CGFloat)relative fontSize:(CGFloat)fontSize +{ + length = [length stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; + NSUInteger stringLength = [length length]; + NSInteger percentIndex = stringLength - 1; + if (stringLength == 0) { + return 0; + } else if ([length characterAtIndex:percentIndex] == '%') { + return (CGFloat)[[length substringWithRange:NSMakeRange(0, percentIndex)] doubleValue] / 100 * relative; + } else { + NSInteger twoLetterUnitIndex = stringLength - 2; + if (twoLetterUnitIndex > 0) { + NSString *lastTwo = [length substringFromIndex:twoLetterUnitIndex]; + NSUInteger end = twoLetterUnitIndex; + CGFloat unit = 1; + if ([lastTwo isEqualToString:@"px"]) { + } else if ([lastTwo isEqualToString:@"em"]) { + unit = fontSize; + } else if ([lastTwo isEqualToString:@"ex"]) { + unit = fontSize / 2; + } else if ([lastTwo isEqualToString:@"pt"]) { + unit = 1.25; + } else if ([lastTwo isEqualToString:@"pc"]) { + unit = 15; + } else if ([lastTwo isEqualToString:@"mm"]) { + unit = (CGFloat)3.543307; + } else if ([lastTwo isEqualToString:@"cm"]) { + unit = (CGFloat)35.43307; + } else if ([lastTwo isEqualToString:@"in"]) { + unit = 90; + } else { + end = stringLength; + } - } else if ([lastTwo isEqualToString:@"em"]) { - unit = fontSize; - } else if ([lastTwo isEqualToString:@"ex"]) { - unit = fontSize / 2; - } else if ([lastTwo isEqualToString:@"pt"]) { - unit = 1.25; - } else if ([lastTwo isEqualToString:@"pc"]) { - unit = 15; - } else if ([lastTwo isEqualToString:@"mm"]) { - unit = (CGFloat)3.543307; - } else if ([lastTwo isEqualToString:@"cm"]) { - unit = (CGFloat)35.43307; - } else if ([lastTwo isEqualToString:@"in"]) { - unit = 90; - } else { - end = stringLength; - } - - return (CGFloat)[[length substringWithRange:NSMakeRange(0, end)] doubleValue] * unit; - } else { - return (CGFloat)[length doubleValue]; - } + return (CGFloat)[[length substringWithRange:NSMakeRange(0, end)] doubleValue] * unit; + } else { + return (CGFloat)[length doubleValue]; } + } } -+ (CGFloat)fromRelative:(RNSVGLength*)length - relative:(CGFloat)relative - fontSize:(CGFloat)fontSize { - RNSVGLengthUnitType unitType = length.unit; - CGFloat value = length.value; - CGFloat unit = 1; - switch (unitType) { - case SVG_LENGTHTYPE_NUMBER: - case SVG_LENGTHTYPE_PX: - return value; - break; ++ (CGFloat)fromRelative:(RNSVGLength *)length relative:(CGFloat)relative fontSize:(CGFloat)fontSize +{ + RNSVGLengthUnitType unitType = length.unit; + CGFloat value = length.value; + CGFloat unit = 1; + switch (unitType) { + case SVG_LENGTHTYPE_NUMBER: + case SVG_LENGTHTYPE_PX: + return value; + break; - case SVG_LENGTHTYPE_PERCENTAGE: - return value / 100 * relative; + case SVG_LENGTHTYPE_PERCENTAGE: + return value / 100 * relative; - case SVG_LENGTHTYPE_EMS: - unit = fontSize; - break; - case SVG_LENGTHTYPE_EXS: - unit = fontSize / 2; - break; + case SVG_LENGTHTYPE_EMS: + unit = fontSize; + break; + case SVG_LENGTHTYPE_EXS: + unit = fontSize / 2; + break; - case SVG_LENGTHTYPE_CM: - unit = (CGFloat)35.43307; - break; - case SVG_LENGTHTYPE_MM: - unit = (CGFloat)3.543307; - break; - case SVG_LENGTHTYPE_IN: - unit = 90; - break; - case SVG_LENGTHTYPE_PT: - unit = 1.25; - break; - case SVG_LENGTHTYPE_PC: - unit = 15; - break; + case SVG_LENGTHTYPE_CM: + unit = (CGFloat)35.43307; + break; + case SVG_LENGTHTYPE_MM: + unit = (CGFloat)3.543307; + break; + case SVG_LENGTHTYPE_IN: + unit = 90; + break; + case SVG_LENGTHTYPE_PT: + unit = 1.25; + break; + case SVG_LENGTHTYPE_PC: + unit = 15; + break; - default: - case SVG_LENGTHTYPE_UNKNOWN: - return value; - } - return value * unit; + default: + case SVG_LENGTHTYPE_UNKNOWN: + return value; + } + return value * unit; } -+ (CGFloat)fromRelative:(RNSVGLength*)length - relative:(CGFloat)relative { - RNSVGLengthUnitType unitType = length.unit; - CGFloat value = length.value; - CGFloat unit = 1; - switch (unitType) { - case SVG_LENGTHTYPE_NUMBER: - case SVG_LENGTHTYPE_PX: - return value; - break; ++ (CGFloat)fromRelative:(RNSVGLength *)length relative:(CGFloat)relative +{ + RNSVGLengthUnitType unitType = length.unit; + CGFloat value = length.value; + CGFloat unit = 1; + switch (unitType) { + case SVG_LENGTHTYPE_NUMBER: + case SVG_LENGTHTYPE_PX: + return value; + break; - case SVG_LENGTHTYPE_PERCENTAGE: - return value / 100 * relative; + case SVG_LENGTHTYPE_PERCENTAGE: + return value / 100 * relative; - case SVG_LENGTHTYPE_EMS: - unit = 12; - break; - case SVG_LENGTHTYPE_EXS: - unit = 6; - break; + case SVG_LENGTHTYPE_EMS: + unit = 12; + break; + case SVG_LENGTHTYPE_EXS: + unit = 6; + break; - case SVG_LENGTHTYPE_CM: - unit = (CGFloat)35.43307; - break; - case SVG_LENGTHTYPE_MM: - unit = (CGFloat)3.543307; - break; - case SVG_LENGTHTYPE_IN: - unit = 90; - break; - case SVG_LENGTHTYPE_PT: - unit = 1.25; - break; - case SVG_LENGTHTYPE_PC: - unit = 15; - break; + case SVG_LENGTHTYPE_CM: + unit = (CGFloat)35.43307; + break; + case SVG_LENGTHTYPE_MM: + unit = (CGFloat)3.543307; + break; + case SVG_LENGTHTYPE_IN: + unit = 90; + break; + case SVG_LENGTHTYPE_PT: + unit = 1.25; + break; + case SVG_LENGTHTYPE_PC: + unit = 15; + break; - default: - case SVG_LENGTHTYPE_UNKNOWN: - return value; - } - return value * unit; + default: + case SVG_LENGTHTYPE_UNKNOWN: + return value; + } + return value * unit; } @end diff --git a/apple/Text/RNSVGTSpan.h b/apple/Text/RNSVGTSpan.h index 5758cebd..eb1d6b42 100644 --- a/apple/Text/RNSVGTSpan.h +++ b/apple/Text/RNSVGTSpan.h @@ -6,8 +6,8 @@ * LICENSE file in the root directory of this source tree. */ -#import #import +#import #import "RNSVGUIKit.h" diff --git a/apple/Text/RNSVGTSpan.mm b/apple/Text/RNSVGTSpan.mm index 7f31892c..206f91c8 100644 --- a/apple/Text/RNSVGTSpan.mm +++ b/apple/Text/RNSVGTSpan.mm @@ -6,36 +6,35 @@ * LICENSE file in the root directory of this source tree. */ #import "RNSVGTSpan.h" -#import "RNSVGUIKit.h" +#import "RNSVGFontData.h" +#import "RNSVGPathMeasure.h" #import "RNSVGText.h" #import "RNSVGTextPath.h" #import "RNSVGTextProperties.h" #import "RNSVGTopAlignedLabel.h" -#import "RNSVGPathMeasure.h" -#import "RNSVGFontData.h" +#import "RNSVGUIKit.h" static NSCharacterSet *RNSVGTSpan_separators = nil; static CGFloat RNSVGTSpan_radToDeg = 180 / (CGFloat)M_PI; #ifdef RN_FABRIC_ENABLED #import -#import "RCTFabricComponentsPlugins.h" -#import "RCTConversions.h" #import +#import "RCTConversions.h" +#import "RCTFabricComponentsPlugins.h" #import "RNSVGFabricConversions.h" #endif // RN_FABRIC_ENABLED -@implementation RNSVGTSpan -{ - CGFloat startOffset; - RNSVGTextPath *textPath; - NSMutableArray *emoji; - NSMutableArray *emojiTransform; - CGFloat cachedAdvance; - CTFontRef fontRef; - CGFloat firstX; - CGFloat firstY; - RNSVGPathMeasure *measure; +@implementation RNSVGTSpan { + CGFloat startOffset; + RNSVGTextPath *textPath; + NSMutableArray *emoji; + NSMutableArray *emojiTransform; + CGFloat cachedAdvance; + CTFontRef fontRef; + CGFloat firstX; + CGFloat firstY; + RNSVGPathMeasure *measure; } #ifdef RN_FABRIC_ENABLED @@ -61,1088 +60,1079 @@ using namespace facebook::react; { const auto &newProps = *std::static_pointer_cast(props); - // textAnchor is in props of VM but not available on component - self.deltaX = createLengthArrayFromStrings(newProps.dx); - self.deltaY = createLengthArrayFromStrings(newProps.dy); - if (!newProps.positionX.empty()) { - self.positionX = createLengthArrayFromStrings(newProps.positionX); - } - if (!newProps.positionY.empty()) { - self.positionY = createLengthArrayFromStrings(newProps.positionY); - } - if (!newProps.x.empty()) { - self.positionX = createLengthArrayFromStrings(newProps.x); - } - if (!newProps.y.empty()) { - self.positionY = createLengthArrayFromStrings(newProps.y); - } - self.rotate = createLengthArrayFromStrings(newProps.rotate); - self.inlineSize = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.inlineSize)]; - self.textLength = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.textLength)]; - self.baselineShift = RCTNSStringFromStringNilIfEmpty(newProps.baselineShift); - self.lengthAdjust = RCTNSStringFromStringNilIfEmpty(newProps.lengthAdjust); - self.alignmentBaseline = RCTNSStringFromStringNilIfEmpty(newProps.alignmentBaseline); - self.content = RCTNSStringFromStringNilIfEmpty(newProps.content); + // textAnchor is in props of VM but not available on component + self.deltaX = createLengthArrayFromStrings(newProps.dx); + self.deltaY = createLengthArrayFromStrings(newProps.dy); + if (!newProps.positionX.empty()) { + self.positionX = createLengthArrayFromStrings(newProps.positionX); + } + if (!newProps.positionY.empty()) { + self.positionY = createLengthArrayFromStrings(newProps.positionY); + } + if (!newProps.x.empty()) { + self.positionX = createLengthArrayFromStrings(newProps.x); + } + if (!newProps.y.empty()) { + self.positionY = createLengthArrayFromStrings(newProps.y); + } + self.rotate = createLengthArrayFromStrings(newProps.rotate); + self.inlineSize = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.inlineSize)]; + self.textLength = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.textLength)]; + self.baselineShift = RCTNSStringFromStringNilIfEmpty(newProps.baselineShift); + self.lengthAdjust = RCTNSStringFromStringNilIfEmpty(newProps.lengthAdjust); + self.alignmentBaseline = RCTNSStringFromStringNilIfEmpty(newProps.alignmentBaseline); + self.content = RCTNSStringFromStringNilIfEmpty(newProps.content); - setCommonGroupProps(newProps, self); + setCommonGroupProps(newProps, self); } - (void)prepareForRecycle { - [super prepareForRecycle]; - - _content = nil; - - startOffset = 0; - textPath = nil; - cachedAdvance = 0; - // these crash, hopefully it is always released by here -// if (fontRef != nil) { -// CFRelease(fontRef); -// } - fontRef = nil; - firstX = 0; - firstY = 0; + [super prepareForRecycle]; - emoji = [NSMutableArray arrayWithCapacity:0]; - emojiTransform = [NSMutableArray arrayWithCapacity:0]; - measure = [[RNSVGPathMeasure alloc]init]; - RNSVGTSpan_separators = [NSCharacterSet whitespaceCharacterSet]; + _content = nil; + startOffset = 0; + textPath = nil; + cachedAdvance = 0; + // these crash, hopefully it is always released by here + // if (fontRef != nil) { + // CFRelease(fontRef); + // } + fontRef = nil; + firstX = 0; + firstY = 0; + + emoji = [NSMutableArray arrayWithCapacity:0]; + emojiTransform = [NSMutableArray arrayWithCapacity:0]; + measure = [[RNSVGPathMeasure alloc] init]; + RNSVGTSpan_separators = [NSCharacterSet whitespaceCharacterSet]; } #endif // RN_FABRIC_ENABLED - (id)init { - self = [super init]; + self = [super init]; - if (RNSVGTSpan_separators == nil) { - RNSVGTSpan_separators = [NSCharacterSet whitespaceCharacterSet]; - } + if (RNSVGTSpan_separators == nil) { + RNSVGTSpan_separators = [NSCharacterSet whitespaceCharacterSet]; + } - emoji = [NSMutableArray arrayWithCapacity:0]; - emojiTransform = [NSMutableArray arrayWithCapacity:0]; - measure = [[RNSVGPathMeasure alloc]init]; + emoji = [NSMutableArray arrayWithCapacity:0]; + emojiTransform = [NSMutableArray arrayWithCapacity:0]; + measure = [[RNSVGPathMeasure alloc] init]; - return self; + return self; } - (void)clearPath { - [super clearPath]; - cachedAdvance = NAN; + [super clearPath]; + cachedAdvance = NAN; } - (void)setContent:(NSString *)content { - if ([content isEqualToString:_content]) { - return; - } - [self invalidate]; - _content = content; + if ([content isEqualToString:_content]) { + return; + } + [self invalidate]; + _content = content; } - (void)renderLayerTo:(CGContextRef)context rect:(CGRect)rect { - if (self.content) { - RNSVGGlyphContext* gc = [self.textRoot getGlyphContext]; - if (self.inlineSize != nil && self.inlineSize.value != 0) { - if (self.fill) { - if (self.fill.class == RNSVGBrush.class) { - CGColorRef color = [self.tintColor CGColor]; - [self drawWrappedText:context gc:gc rect:rect color:color]; - } else { - CGColorRef color = [self.fill getColorWithOpacity:self.fillOpacity]; - [self drawWrappedText:context gc:gc rect:rect color:color]; - CGColorRelease(color); - } - } - if (self.stroke) { - if (self.stroke.class == RNSVGBrush.class) { - CGColorRef color = [self.tintColor CGColor]; - [self drawWrappedText:context gc:gc rect:rect color:color]; - } else { - CGColorRef color = [self.stroke getColorWithOpacity:self.strokeOpacity]; - [self drawWrappedText:context gc:gc rect:rect color:color]; - CGColorRelease(color); - } - } + if (self.content) { + RNSVGGlyphContext *gc = [self.textRoot getGlyphContext]; + if (self.inlineSize != nil && self.inlineSize.value != 0) { + if (self.fill) { + if (self.fill.class == RNSVGBrush.class) { + CGColorRef color = [self.tintColor CGColor]; + [self drawWrappedText:context gc:gc rect:rect color:color]; } else { - if (self.path) { - NSUInteger count = [emoji count]; - CGFloat fontSize = [gc getFontSize]; - for (NSUInteger i = 0; i < count; i++) { - RNSVGPlatformView *label = [emoji objectAtIndex:i]; - NSValue *transformValue = [emojiTransform objectAtIndex:i]; - CGAffineTransform transform = [transformValue CGAffineTransformValue]; - CGContextConcatCTM(context, transform); - CGContextTranslateCTM(context, 0, -fontSize); - [label.layer renderInContext:context]; - CGContextTranslateCTM(context, 0, fontSize); - CGContextConcatCTM(context, CGAffineTransformInvert(transform)); - } - } - [self renderPathTo:context rect:rect]; + CGColorRef color = [self.fill getColorWithOpacity:self.fillOpacity]; + [self drawWrappedText:context gc:gc rect:rect color:color]; + CGColorRelease(color); } + } + if (self.stroke) { + if (self.stroke.class == RNSVGBrush.class) { + CGColorRef color = [self.tintColor CGColor]; + [self drawWrappedText:context gc:gc rect:rect color:color]; + } else { + CGColorRef color = [self.stroke getColorWithOpacity:self.strokeOpacity]; + [self drawWrappedText:context gc:gc rect:rect color:color]; + CGColorRelease(color); + } + } } else { - [self clip:context]; - [self renderGroupTo:context rect:rect]; + if (self.path) { + NSUInteger count = [emoji count]; + CGFloat fontSize = [gc getFontSize]; + for (NSUInteger i = 0; i < count; i++) { + RNSVGPlatformView *label = [emoji objectAtIndex:i]; + NSValue *transformValue = [emojiTransform objectAtIndex:i]; + CGAffineTransform transform = [transformValue CGAffineTransformValue]; + CGContextConcatCTM(context, transform); + CGContextTranslateCTM(context, 0, -fontSize); + [label.layer renderInContext:context]; + CGContextTranslateCTM(context, 0, fontSize); + CGContextConcatCTM(context, CGAffineTransformInvert(transform)); + } + } + [self renderPathTo:context rect:rect]; } + } else { + [self clip:context]; + [self renderGroupTo:context rect:rect]; + } } -- (NSMutableDictionary *)getAttributes:(RNSVGFontData *)fontdata { - NSMutableDictionary *attrs = [[NSMutableDictionary alloc] init]; +- (NSMutableDictionary *)getAttributes:(RNSVGFontData *)fontdata +{ + NSMutableDictionary *attrs = [[NSMutableDictionary alloc] init]; - if (fontRef != nil) { - attrs[NSFontAttributeName] = (__bridge id)fontRef; - } + if (fontRef != nil) { + attrs[NSFontAttributeName] = (__bridge id)fontRef; + } - CGFloat letterSpacing = fontdata->letterSpacing; - bool allowOptionalLigatures = letterSpacing == 0 && fontdata->fontVariantLigatures == RNSVGFontVariantLigaturesNormal; - NSNumber *lig = [NSNumber numberWithInt:allowOptionalLigatures ? 2 : 1]; - attrs[NSLigatureAttributeName] = lig; + CGFloat letterSpacing = fontdata->letterSpacing; + bool allowOptionalLigatures = letterSpacing == 0 && fontdata->fontVariantLigatures == RNSVGFontVariantLigaturesNormal; + NSNumber *lig = [NSNumber numberWithInt:allowOptionalLigatures ? 2 : 1]; + attrs[NSLigatureAttributeName] = lig; - CGFloat kerning = fontdata->kerning; - float kern = (float)(letterSpacing + kerning); - NSNumber *kernAttr = [NSNumber numberWithFloat:kern]; + CGFloat kerning = fontdata->kerning; + float kern = (float)(letterSpacing + kerning); + NSNumber *kernAttr = [NSNumber numberWithFloat:kern]; #if DTCORETEXT_SUPPORT_NS_ATTRIBUTES - if (___useiOS6Attributes) - { - [attrs setObject:kernAttr forKey:NSKernAttributeName]; - } - else + if (___useiOS6Attributes) { + [attrs setObject:kernAttr forKey:NSKernAttributeName]; + } else #endif // DTCORETEXT_SUPPORT_NS_ATTRIBUTES - { - [attrs setObject:kernAttr forKey:(id)kCTKernAttributeName]; - } + { + [attrs setObject:kernAttr forKey:(id)kCTKernAttributeName]; + } - return attrs; + return attrs; } RNSVGTopAlignedLabel *label; -- (void)drawWrappedText:(CGContextRef)context gc:(RNSVGGlyphContext *)gc rect:(CGRect)rect color:(CGColorRef)color { - [self pushGlyphContext]; - if (fontRef != nil) { - CFRelease(fontRef); - } - fontRef = [self getFontFromContext]; - RNSVGFontData* fontdata = [gc getFont]; - CFStringRef string = (__bridge CFStringRef)self.content; - NSMutableDictionary * attrs = [self getAttributes:fontdata]; - CFMutableDictionaryRef attributes = (__bridge CFMutableDictionaryRef)attrs; - CFAttributedStringRef attrString = CFAttributedStringCreate(kCFAllocatorDefault, string, attributes); +- (void)drawWrappedText:(CGContextRef)context gc:(RNSVGGlyphContext *)gc rect:(CGRect)rect color:(CGColorRef)color +{ + [self pushGlyphContext]; + if (fontRef != nil) { + CFRelease(fontRef); + } + fontRef = [self getFontFromContext]; + RNSVGFontData *fontdata = [gc getFont]; + CFStringRef string = (__bridge CFStringRef)self.content; + NSMutableDictionary *attrs = [self getAttributes:fontdata]; + CFMutableDictionaryRef attributes = (__bridge CFMutableDictionaryRef)attrs; + CFAttributedStringRef attrString = CFAttributedStringCreate(kCFAllocatorDefault, string, attributes); - enum RNSVGTextAnchor textAnchor = fontdata->textAnchor; - NSTextAlignment align; - switch (textAnchor) { - case RNSVGTextAnchorStart: - align = NSTextAlignmentLeft; - break; + enum RNSVGTextAnchor textAnchor = fontdata->textAnchor; + NSTextAlignment align; + switch (textAnchor) { + case RNSVGTextAnchorStart: + align = NSTextAlignmentLeft; + break; - case RNSVGTextAnchorMiddle: - align = NSTextAlignmentCenter; - break; + case RNSVGTextAnchorMiddle: + align = NSTextAlignmentCenter; + break; - case RNSVGTextAnchorEnd: - align = NSTextAlignmentRight; - break; + case RNSVGTextAnchorEnd: + align = NSTextAlignmentRight; + break; - default: - align = NSTextAlignmentLeft; - break; - } + default: + align = NSTextAlignmentLeft; + break; + } - UIFont *font = (__bridge UIFont *)(fontRef); - if (!label) { - label = [[RNSVGTopAlignedLabel alloc] init]; - } - label.attributedText = (__bridge NSAttributedString * _Nullable)(attrString); - label.lineBreakMode = NSLineBreakByWordWrapping; - label.backgroundColor = RNSVGColor.clearColor; - label.textAlignment = align; - label.numberOfLines = 0; + UIFont *font = (__bridge UIFont *)(fontRef); + if (!label) { + label = [[RNSVGTopAlignedLabel alloc] init]; + } + label.attributedText = (__bridge NSAttributedString *_Nullable)(attrString); + label.lineBreakMode = NSLineBreakByWordWrapping; + label.backgroundColor = RNSVGColor.clearColor; + label.textAlignment = align; + label.numberOfLines = 0; #if !TARGET_OS_OSX // On macOS, views are transparent by default - label.opaque = NO; + label.opaque = NO; #endif // TARGET_OS_OSX - label.font = font; - label.textColor = [RNSVGColor colorWithCGColor:color]; + label.font = font; + label.textColor = [RNSVGColor colorWithCGColor:color]; - CGFloat fontSize = [gc getFontSize]; - CGFloat height = CGRectGetHeight(rect); - CGFloat width = [RNSVGPropHelper fromRelative:self.inlineSize - relative:[gc getWidth] - fontSize:fontSize]; - CGRect constrain = CGRectMake(0, 0, width, height); - CGRect s = [self.content - boundingRectWithSize:constrain.size - options:NSStringDrawingUsesLineFragmentOrigin - attributes:attrs - context:nil]; + CGFloat fontSize = [gc getFontSize]; + CGFloat height = CGRectGetHeight(rect); + CGFloat width = [RNSVGPropHelper fromRelative:self.inlineSize relative:[gc getWidth] fontSize:fontSize]; + CGRect constrain = CGRectMake(0, 0, width, height); + CGRect s = [self.content boundingRectWithSize:constrain.size + options:NSStringDrawingUsesLineFragmentOrigin + attributes:attrs + context:nil]; - CGRect bounds = CGRectMake(0, 0, width, s.size.height); - label.frame = bounds; - label.bounds = bounds; + CGRect bounds = CGRectMake(0, 0, width, s.size.height); + label.frame = bounds; + label.bounds = bounds; - firstX = [gc nextXWithDouble:0]; - firstY = [gc nextY] - font.ascender; - CGFloat dx = firstX; - CGFloat dy = firstY; - CGContextTranslateCTM(context, dx, dy); - [label.layer renderInContext:context]; - CGContextTranslateCTM(context, -dx, -dy); - [self popGlyphContext]; - [self renderPathTo:context rect:rect]; + firstX = [gc nextXWithDouble:0]; + firstY = [gc nextY] - font.ascender; + CGFloat dx = firstX; + CGFloat dy = firstY; + CGContextTranslateCTM(context, dx, dy); + [label.layer renderInContext:context]; + CGContextTranslateCTM(context, -dx, -dy); + [self popGlyphContext]; + [self renderPathTo:context rect:rect]; } - (CGPathRef)getPath:(CGContextRef)context { - CGPathRef path = self.path; - if (path) { - return path; - } - - NSString *text = self.content; - if (!text) { - return [self getGroupPath:context]; - } - - if (self.inlineSize != nil && self.inlineSize.value != 0) { - CGAffineTransform transform = CGAffineTransformMakeTranslation(firstX, firstY); - path = CGPathCreateWithRect(label.bounds, &transform); - self.path = CGPathRetain(path); - self.skip = true; - return path; - } - - [self setupTextPath:context]; - - [self pushGlyphContext]; - - path = [self getLinePath:text context:context]; - - self.path = CGPathRetain(path); - - [self popGlyphContext]; - + CGPathRef path = self.path; + if (path) { return path; + } + + NSString *text = self.content; + if (!text) { + return [self getGroupPath:context]; + } + + if (self.inlineSize != nil && self.inlineSize.value != 0) { + CGAffineTransform transform = CGAffineTransformMakeTranslation(firstX, firstY); + path = CGPathCreateWithRect(label.bounds, &transform); + self.path = CGPathRetain(path); + self.skip = true; + return path; + } + + [self setupTextPath:context]; + + [self pushGlyphContext]; + + path = [self getLinePath:text context:context]; + + self.path = CGPathRetain(path); + + [self popGlyphContext]; + + return path; } - (CGFloat)getSubtreeTextChunksTotalAdvance { - if (!isnan(cachedAdvance)) { - return cachedAdvance; + if (!isnan(cachedAdvance)) { + return cachedAdvance; + } + CGFloat advance = 0; + + NSString *str = self.content; + if (!str) { + for (RNSVGView *node in self.subviews) { + if ([node isKindOfClass:[RNSVGText class]]) { + RNSVGText *text = (RNSVGText *)node; + advance += [text getSubtreeTextChunksTotalAdvance]; + } } - CGFloat advance = 0; + cachedAdvance = advance; + return advance; + } - NSString *str = self.content; - if (!str) { - for (RNSVGView *node in self.subviews) { - if ([node isKindOfClass:[RNSVGText class]]) { - RNSVGText *text = (RNSVGText*)node; - advance += [text getSubtreeTextChunksTotalAdvance]; - } - } - cachedAdvance = advance; - return advance; - } + // Create a dictionary for this font + fontRef = [self getFontFromContext]; + RNSVGGlyphContext *gc = [self.textRoot getGlyphContext]; + RNSVGFontData *fontdata = [gc getFont]; + NSMutableDictionary *attrs = [self getAttributes:fontdata]; - // Create a dictionary for this font - fontRef = [self getFontFromContext]; - RNSVGGlyphContext *gc = [self.textRoot getGlyphContext]; - RNSVGFontData *fontdata = [gc getFont]; - NSMutableDictionary *attrs = [self getAttributes:fontdata]; + CFStringRef string = (__bridge CFStringRef)str; + CFMutableDictionaryRef attributes = (__bridge CFMutableDictionaryRef)attrs; + CFAttributedStringRef attrString = CFAttributedStringCreate(kCFAllocatorDefault, string, attributes); + CTLineRef line = CTLineCreateWithAttributedString(attrString); - CFStringRef string = (__bridge CFStringRef)str; - CFMutableDictionaryRef attributes = (__bridge CFMutableDictionaryRef)attrs; - CFAttributedStringRef attrString = CFAttributedStringCreate(kCFAllocatorDefault, string, attributes); - CTLineRef line = CTLineCreateWithAttributedString(attrString); + CGRect textBounds = CTLineGetBoundsWithOptions(line, 0); + CGFloat textMeasure = CGRectGetWidth(textBounds); + cachedAdvance = textMeasure; - CGRect textBounds = CTLineGetBoundsWithOptions(line, 0); - CGFloat textMeasure = CGRectGetWidth(textBounds); - cachedAdvance = textMeasure; + CFRelease(attrString); + CFRelease(line); - CFRelease(attrString); - CFRelease(line); - - return textMeasure; + return textMeasure; } - (CGPathRef)getLinePath:(NSString *)str context:(CGContextRef)context { - // Create a dictionary for this font - fontRef = [self getFontFromContext]; - CGMutablePathRef path = CGPathCreateMutable(); - RNSVGGlyphContext *gc = [self.textRoot getGlyphContext]; - RNSVGFontData *font = [gc getFont]; - NSUInteger n = str.length; - /* - * - * Three properties affect the space between characters and words: - * - * ‘kerning’ indicates whether the user agent should adjust inter-glyph spacing - * based on kerning tables that are included in the relevant font - * (i.e., enable auto-kerning) or instead disable auto-kerning - * and instead set inter-character spacing to a specific length (typically, zero). - * - * ‘letter-spacing’ indicates an amount of space that is to be added between text - * characters supplemental to any spacing due to the ‘kerning’ property. - * - * ‘word-spacing’ indicates the spacing behavior between words. - * - * Letter-spacing is applied after bidi reordering and is in addition to any word-spacing. - * Depending on the justification rules in effect, user agents may further increase - * or decrease the space between typographic character units in order to justify text. - * - * */ - CGFloat kerning = font->kerning; - CGFloat wordSpacing = font->wordSpacing; - CGFloat letterSpacing = font->letterSpacing; - bool autoKerning = !font->manualKerning; + // Create a dictionary for this font + fontRef = [self getFontFromContext]; + CGMutablePathRef path = CGPathCreateMutable(); + RNSVGGlyphContext *gc = [self.textRoot getGlyphContext]; + RNSVGFontData *font = [gc getFont]; + NSUInteger n = str.length; + /* + * + * Three properties affect the space between characters and words: + * + * ‘kerning’ indicates whether the user agent should adjust inter-glyph spacing + * based on kerning tables that are included in the relevant font + * (i.e., enable auto-kerning) or instead disable auto-kerning + * and instead set inter-character spacing to a specific length (typically, zero). + * + * ‘letter-spacing’ indicates an amount of space that is to be added between text + * characters supplemental to any spacing due to the ‘kerning’ property. + * + * ‘word-spacing’ indicates the spacing behavior between words. + * + * Letter-spacing is applied after bidi reordering and is in addition to any word-spacing. + * Depending on the justification rules in effect, user agents may further increase + * or decrease the space between typographic character units in order to justify text. + * + * */ + CGFloat kerning = font->kerning; + CGFloat wordSpacing = font->wordSpacing; + CGFloat letterSpacing = font->letterSpacing; + bool autoKerning = !font->manualKerning; - /* - 11.1.2. Fonts and glyphs + /* + 11.1.2. Fonts and glyphs - A font consists of a collection of glyphs together with other information (collectively, - the font tables) necessary to use those glyphs to present characters on some visual medium. + A font consists of a collection of glyphs together with other information (collectively, + the font tables) necessary to use those glyphs to present characters on some visual medium. - The combination of the collection of glyphs and the font tables is called the font data. + The combination of the collection of glyphs and the font tables is called the font data. - A font may supply substitution and positioning tables that can be used by a formatter - (text shaper) to re-order, combine and position a sequence of glyphs to form one or more - composite glyphs. + A font may supply substitution and positioning tables that can be used by a formatter + (text shaper) to re-order, combine and position a sequence of glyphs to form one or more + composite glyphs. - The combining may be as simple as a ligature, or as complex as an indic syllable which - combines, usually with some re-ordering, multiple consonants and vowel glyphs. + The combining may be as simple as a ligature, or as complex as an indic syllable which + combines, usually with some re-ordering, multiple consonants and vowel glyphs. - The tables may be language dependent, allowing the use of language appropriate letter forms. + The tables may be language dependent, allowing the use of language appropriate letter forms. - When a glyph, simple or composite, represents an indivisible unit for typesetting purposes, - it is know as a typographic character. + When a glyph, simple or composite, represents an indivisible unit for typesetting purposes, + it is know as a typographic character. - Ligatures are an important feature of advance text layout. + Ligatures are an important feature of advance text layout. - Some ligatures are discretionary while others (e.g. in Arabic) are required. + Some ligatures are discretionary while others (e.g. in Arabic) are required. - The following explicit rules apply to ligature formation: + The following explicit rules apply to ligature formation: - Ligature formation should not be enabled when characters are in different DOM text nodes; - thus, characters separated by markup should not use ligatures. + Ligature formation should not be enabled when characters are in different DOM text nodes; + thus, characters separated by markup should not use ligatures. - Ligature formation should not be enabled when characters are in different text chunks. + Ligature formation should not be enabled when characters are in different text chunks. - Discretionary ligatures should not be used when the spacing between two characters is not - the same as the default space (e.g. when letter-spacing has a non-default value, - or text-align has a value of justify and text-justify has a value of distribute). - (See CSS Text Module Level 3, ([css-text-3]). + Discretionary ligatures should not be used when the spacing between two characters is not + the same as the default space (e.g. when letter-spacing has a non-default value, + or text-align has a value of justify and text-justify has a value of distribute). + (See CSS Text Module Level 3, ([css-text-3]). - SVG attributes such as ‘dx’, ‘textLength’, and ‘spacing’ (in ‘textPath’) that may reposition - typographic characters do not break discretionary ligatures. + SVG attributes such as ‘dx’, ‘textLength’, and ‘spacing’ (in ‘textPath’) that may reposition + typographic characters do not break discretionary ligatures. - If discretionary ligatures are not desired - they can be turned off by using the font-variant-ligatures property. + If discretionary ligatures are not desired + they can be turned off by using the font-variant-ligatures property. - When the effective letter-spacing between two characters is not zero - (due to either justification or non-zero computed ‘letter-spacing’), - user agents should not apply optional ligatures. - https://www.w3.org/TR/css-text-3/#letter-spacing-property - */ - bool allowOptionalLigatures = letterSpacing == 0 && font->fontVariantLigatures == RNSVGFontVariantLigaturesNormal; + When the effective letter-spacing between two characters is not zero + (due to either justification or non-zero computed ‘letter-spacing’), + user agents should not apply optional ligatures. + https://www.w3.org/TR/css-text-3/#letter-spacing-property + */ + bool allowOptionalLigatures = letterSpacing == 0 && font->fontVariantLigatures == RNSVGFontVariantLigaturesNormal; - /* - For OpenType fonts, discretionary ligatures include those enabled by - the liga, clig, dlig, hlig, and cala features; - required ligatures are found in the rlig feature. - https://svgwg.org/svg2-draft/text.html#FontsGlyphs + /* + For OpenType fonts, discretionary ligatures include those enabled by + the liga, clig, dlig, hlig, and cala features; + required ligatures are found in the rlig feature. + https://svgwg.org/svg2-draft/text.html#FontsGlyphs - http://dev.w3.org/csswg/css-fonts/#propdef-font-feature-settings + http://dev.w3.org/csswg/css-fonts/#propdef-font-feature-settings - https://www.microsoft.com/typography/otspec/featurelist.htm - https://www.microsoft.com/typography/otspec/featuretags.htm - https://www.microsoft.com/typography/otspec/features_pt.htm - https://www.microsoft.com/typography/otfntdev/arabicot/features.aspx - http://unifraktur.sourceforge.net/testcases/enable_opentype_features/ - https://en.wikipedia.org/wiki/List_of_typographic_features - http://ilovetypography.com/OpenType/opentype-features.html - https://www.typotheque.com/articles/opentype_features_in_css - https://practice.typekit.com/lesson/caring-about-opentype-features/ - http://stateofwebtype.com/ + https://www.microsoft.com/typography/otspec/featurelist.htm + https://www.microsoft.com/typography/otspec/featuretags.htm + https://www.microsoft.com/typography/otspec/features_pt.htm + https://www.microsoft.com/typography/otfntdev/arabicot/features.aspx + http://unifraktur.sourceforge.net/testcases/enable_opentype_features/ + https://en.wikipedia.org/wiki/List_of_typographic_features + http://ilovetypography.com/OpenType/opentype-features.html + https://www.typotheque.com/articles/opentype_features_in_css + https://practice.typekit.com/lesson/caring-about-opentype-features/ + http://stateofwebtype.com/ - 6.12. Low-level font feature settings control: the font-feature-settings property + 6.12. Low-level font feature settings control: the font-feature-settings property - Name: font-feature-settings - Value: normal | # - Initial: normal - Applies to: all elements - Inherited: yes - Percentages: N/A - Media: visual - Computed value: as specified - Animatable: no + Name: font-feature-settings + Value: normal | # + Initial: normal + Applies to: all elements + Inherited: yes + Percentages: N/A + Media: visual + Computed value: as specified + Animatable: no - https://drafts.csswg.org/css-fonts-3/#default-features + https://drafts.csswg.org/css-fonts-3/#default-features - 7.1. Default features + 7.1. Default features - For OpenType fonts, user agents must enable the default features defined in the OpenType - documentation for a given script and writing mode. + For OpenType fonts, user agents must enable the default features defined in the OpenType + documentation for a given script and writing mode. - Required ligatures, common ligatures and contextual forms must be enabled by default - (OpenType features: rlig, liga, clig, calt), - along with localized forms (OpenType feature: locl), - and features required for proper display of composed characters and marks - (OpenType features: ccmp, mark, mkmk). + Required ligatures, common ligatures and contextual forms must be enabled by default + (OpenType features: rlig, liga, clig, calt), + along with localized forms (OpenType feature: locl), + and features required for proper display of composed characters and marks + (OpenType features: ccmp, mark, mkmk). - These features must always be enabled, even when the value of the ‘font-variant’ and - ‘font-feature-settings’ properties is ‘normal’. + These features must always be enabled, even when the value of the ‘font-variant’ and + ‘font-feature-settings’ properties is ‘normal’. - Individual features are only disabled when explicitly overridden by the author, - as when ‘font-variant-ligatures’ is set to ‘no-common-ligatures’. + Individual features are only disabled when explicitly overridden by the author, + as when ‘font-variant-ligatures’ is set to ‘no-common-ligatures’. - TODO For handling complex scripts such as Arabic, Mongolian or Devanagari additional features - are required. + TODO For handling complex scripts such as Arabic, Mongolian or Devanagari additional features + are required. - TODO For upright text within vertical text runs, - vertical alternates (OpenType feature: vert) must be enabled. - */ - // OpenType.js font data - NSDictionary * fontData = font->fontData; - NSMutableDictionary *attrs = [[NSMutableDictionary alloc] init]; - CFMutableDictionaryRef attributes = (__bridge CFMutableDictionaryRef)attrs; + TODO For upright text within vertical text runs, + vertical alternates (OpenType feature: vert) must be enabled. + */ + // OpenType.js font data + NSDictionary *fontData = font->fontData; + NSMutableDictionary *attrs = [[NSMutableDictionary alloc] init]; + CFMutableDictionaryRef attributes = (__bridge CFMutableDictionaryRef)attrs; - NSNumber *lig = [NSNumber numberWithInt:allowOptionalLigatures ? 2 : 1]; - attrs[NSLigatureAttributeName] = lig; - if (fontRef != nil) { - attrs[NSFontAttributeName] = (__bridge id)fontRef; - } - if (!autoKerning) { - NSNumber *noAutoKern = [NSNumber numberWithFloat:0.0f]; + NSNumber *lig = [NSNumber numberWithInt:allowOptionalLigatures ? 2 : 1]; + attrs[NSLigatureAttributeName] = lig; + if (fontRef != nil) { + attrs[NSFontAttributeName] = (__bridge id)fontRef; + } + if (!autoKerning) { + NSNumber *noAutoKern = [NSNumber numberWithFloat:0.0f]; #if DTCORETEXT_SUPPORT_NS_ATTRIBUTES - if (___useiOS6Attributes) - { - [attrs setObject:noAutoKern forKey:NSKernAttributeName]; - } - else + if (___useiOS6Attributes) { + [attrs setObject:noAutoKern forKey:NSKernAttributeName]; + } else #endif // DTCORETEXT_SUPPORT_NS_ATTRIBUTES - { - [attrs setObject:noAutoKern forKey:(id)kCTKernAttributeName]; - } + { + [attrs setObject:noAutoKern forKey:(id)kCTKernAttributeName]; } + } - CFStringRef string = (__bridge CFStringRef)str; - CFAttributedStringRef attrString = CFAttributedStringCreate(kCFAllocatorDefault, string, attributes); - CTLineRef line = CTLineCreateWithAttributedString(attrString); + CFStringRef string = (__bridge CFStringRef)str; + CFAttributedStringRef attrString = CFAttributedStringCreate(kCFAllocatorDefault, string, attributes); + CTLineRef line = CTLineCreateWithAttributedString(attrString); - /* - Determine the startpoint-on-the-path for the first glyph using attribute ‘startOffset’ - and property text-anchor. + /* + Determine the startpoint-on-the-path for the first glyph using attribute ‘startOffset’ + and property text-anchor. - For text-anchor:start, startpoint-on-the-path is the point - on the path which represents the point on the path which is ‘startOffset’ distance - along the path from the start of the path, calculated using the user agent's distance - along the path algorithm. + For text-anchor:start, startpoint-on-the-path is the point + on the path which represents the point on the path which is ‘startOffset’ distance + along the path from the start of the path, calculated using the user agent's distance + along the path algorithm. - For text-anchor:middle, startpoint-on-the-path is the point - on the path which represents the point on the path which is [ ‘startOffset’ minus half - of the total advance values for all of the glyphs in the ‘textPath’ element ] distance - along the path from the start of the path, calculated using the user agent's distance - along the path algorithm. + For text-anchor:middle, startpoint-on-the-path is the point + on the path which represents the point on the path which is [ ‘startOffset’ minus half + of the total advance values for all of the glyphs in the ‘textPath’ element ] distance + along the path from the start of the path, calculated using the user agent's distance + along the path algorithm. - For text-anchor:end, startpoint-on-the-path is the point on - the path which represents the point on the path which is [ ‘startOffset’ minus the - total advance values for all of the glyphs in the ‘textPath’ element ]. + For text-anchor:end, startpoint-on-the-path is the point on + the path which represents the point on the path which is [ ‘startOffset’ minus the + total advance values for all of the glyphs in the ‘textPath’ element ]. - Before rendering the first glyph, the horizontal component of the startpoint-on-the-path - is adjusted to take into account various horizontal alignment text properties and - attributes, such as a ‘dx’ attribute value on a ‘tspan’ element. - */ - enum RNSVGTextAnchor textAnchor = font->textAnchor; - RNSVGText *anchorRoot = [self getTextAnchorRoot]; - CGFloat textMeasure = [anchorRoot getSubtreeTextChunksTotalAdvance]; - CGFloat offset = [RNSVGTSpan getTextAnchorOffset:textAnchor width:textMeasure]; + Before rendering the first glyph, the horizontal component of the startpoint-on-the-path + is adjusted to take into account various horizontal alignment text properties and + attributes, such as a ‘dx’ attribute value on a ‘tspan’ element. + */ + enum RNSVGTextAnchor textAnchor = font->textAnchor; + RNSVGText *anchorRoot = [self getTextAnchorRoot]; + CGFloat textMeasure = [anchorRoot getSubtreeTextChunksTotalAdvance]; + CGFloat offset = [RNSVGTSpan getTextAnchorOffset:textAnchor width:textMeasure]; - bool hasTextPath = textPath != nil; - - int side = 1; - CGFloat startOfRendering = 0; - CGFloat endOfRendering = measure.pathLength; - CGFloat fontSize = [gc getFontSize]; - //bool sharpMidLine = false; - if (hasTextPath) { - //sharpMidLine = RNSVGTextPathMidLineFromString([textPath midLine]) == RNSVGTextPathMidLineSharp; - /* - Name - side - Value - left | right - initial value - left - Animatable - yes - - Determines the side of the path the text is placed on - (relative to the path direction). - - Specifying a value of right effectively reverses the path. - - Added in SVG 2 to allow text either inside or outside closed subpaths - and basic shapes (e.g. rectangles, circles, and ellipses). - - Adding 'side' was resolved at the Sydney (2015) meeting. - */ - side = RNSVGTextPathSideFromString([textPath side]) == RNSVGTextPathSideRight ? -1 : 1; - /* - Name - startOffset - Value - | | - initial value - 0 - Animatable - yes - - An offset from the start of the path for the initial current text position, - calculated using the user agent's distance along the path algorithm, - after converting the path to the ‘textPath’ element's coordinate system. - - If a other than a percentage is given, then the ‘startOffset’ - represents a distance along the path measured in the current user coordinate - system for the ‘textPath’ element. - - If a percentage is given, then the ‘startOffset’ represents a percentage - distance along the entire path. Thus, startOffset="0%" indicates the start - point of the path and startOffset="100%" indicates the end point of the path. - - Negative values and values larger than the path length (e.g. 150%) are allowed. - - Any typographic characters with mid-points that are not on the path are not rendered - - For paths consisting of a single closed subpath (including an equivalent path for a - basic shape), typographic characters are rendered along one complete circuit of the - path. The text is aligned as determined by the text-anchor property to a position - along the path set by the ‘startOffset’ attribute. - - For the start (end) value, the text is rendered from the start (end) of the line - until the initial position along the path is reached again. - - For the middle, the text is rendered from the middle point in both directions until - a point on the path equal distance in both directions from the initial position on - the path is reached. - */ - CGFloat absoluteStartOffset = [RNSVGPropHelper fromRelative:textPath.startOffset - relative:measure.pathLength - fontSize:fontSize]; - offset += absoluteStartOffset; - if (measure.isClosed) { - CGFloat halfPathDistance = measure.pathLength / 2; - startOfRendering = absoluteStartOffset + (textAnchor == RNSVGTextAnchorMiddle ? -halfPathDistance : 0); - endOfRendering = startOfRendering + measure.pathLength; - } - /* - RNSVGTextPathSpacing spacing = textPath.getSpacing(); - if (spacing == RNSVGTextPathSpacing.auto) { - // Hmm, what to do here? - // https://svgwg.org/svg2-draft/text.html#TextPathElementSpacingAttribute - } - */ - } + bool hasTextPath = textPath != nil; + int side = 1; + CGFloat startOfRendering = 0; + CGFloat endOfRendering = measure.pathLength; + CGFloat fontSize = [gc getFontSize]; + // bool sharpMidLine = false; + if (hasTextPath) { + // sharpMidLine = RNSVGTextPathMidLineFromString([textPath midLine]) == RNSVGTextPathMidLineSharp; /* Name - method + side Value - align | stretch + left | right initial value - align + left Animatable yes - Indicates the method by which text should be rendered along the path. - A value of align indicates that the typographic character should be rendered using - simple 2×3 matrix transformations such that there is no stretching/warping of the - typographic characters. Typically, supplemental rotation, scaling and translation - transformations are done for each typographic characters to be rendered. + Determines the side of the path the text is placed on + (relative to the path direction). - As a result, with align, in fonts where the typographic characters are designed to be - connected (e.g., cursive fonts), the connections may not align properly when text is - rendered along a path. + Specifying a value of right effectively reverses the path. - A value of stretch indicates that the typographic character outlines will be converted - into paths, and then all end points and control points will be adjusted to be along the - perpendicular vectors from the path, thereby stretching and possibly warping the glyphs. + Added in SVG 2 to allow text either inside or outside closed subpaths + and basic shapes (e.g. rectangles, circles, and ellipses). - With this approach, connected typographic characters, such as in cursive scripts, - will maintain their connections. (Non-vertical straight path segments should be - converted to Bézier curves in such a way that horizontal straight paths have an - (approximately) constant offset from the path along which the typographic characters - are rendered.) - - TODO implement stretch + Adding 'side' was resolved at the Sydney (2015) meeting. */ - + side = RNSVGTextPathSideFromString([textPath side]) == RNSVGTextPathSideRight ? -1 : 1; /* - Name Value Initial value Animatable - textLength | | See below yes + Name + startOffset + Value + | | + initial value + 0 + Animatable + yes - The author's computation of the total sum of all of the advance values that correspond - to character data within this element, including the advance value on the glyph - (horizontal or vertical), the effect of properties letter-spacing and word-spacing and - adjustments due to attributes ‘dx’ and ‘dy’ on this ‘text’ or ‘tspan’ element or any - descendants. This value is used to calibrate the user agent's own calculations with - that of the author. + An offset from the start of the path for the initial current text position, + calculated using the user agent's distance along the path algorithm, + after converting the path to the ‘textPath’ element's coordinate system. - The purpose of this attribute is to allow the author to achieve exact alignment, - in visual rendering order after any bidirectional reordering, for the first and - last rendered glyphs that correspond to this element; thus, for the last rendered - character (in visual rendering order after any bidirectional reordering), - any supplemental inter-character spacing beyond normal glyph advances are ignored - (in most cases) when the user agent determines the appropriate amount to expand/compress - the text string to fit within a length of ‘textLength’. + If a other than a percentage is given, then the ‘startOffset’ + represents a distance along the path measured in the current user coordinate + system for the ‘textPath’ element. - If attribute ‘textLength’ is specified on a given element and also specified on an - ancestor, the adjustments on all character data within this element are controlled by - the value of ‘textLength’ on this element exclusively, with the possible side-effect - that the adjustment ratio for the contents of this element might be different than the - adjustment ratio used for other content that shares the same ancestor. The user agent - must assume that the total advance values for the other content within that ancestor is - the difference between the advance value on that ancestor and the advance value for - this element. + If a percentage is given, then the ‘startOffset’ represents a percentage + distance along the entire path. Thus, startOffset="0%" indicates the start + point of the path and startOffset="100%" indicates the end point of the path. - This attribute is not intended for use to obtain effects such as shrinking or - expanding text. + Negative values and values larger than the path length (e.g. 150%) are allowed. - A negative value is an error (see Error processing). + Any typographic characters with mid-points that are not on the path are not rendered - The ‘textLength’ attribute is only applied when the wrapping area is not defined by the - TODO shape-inside or the inline-size properties. It is also not applied for any ‘text’ or - TODO ‘tspan’ element that has forced line breaks (due to a white-space value of pre or - pre-line). + For paths consisting of a single closed subpath (including an equivalent path for a + basic shape), typographic characters are rendered along one complete circuit of the + path. The text is aligned as determined by the text-anchor property to a position + along the path set by the ‘startOffset’ attribute. - If the attribute is not specified anywhere within a ‘text’ element, the effect is as if - the author's computation exactly matched the value calculated by the user agent; - thus, no advance adjustments are made. + For the start (end) value, the text is rendered from the start (end) of the line + until the initial position along the path is reached again. + + For the middle, the text is rendered from the middle point in both directions until + a point on the path equal distance in both directions from the initial position on + the path is reached. */ - CGFloat scaleSpacingAndGlyphs = 1; - RNSVGLength *mTextLength = [self textLength]; - enum RNSVGTextLengthAdjust mLengthAdjust = RNSVGTextLengthAdjustFromString([self lengthAdjust]); - if (mTextLength != nil) { - CGFloat author = [RNSVGPropHelper fromRelative:mTextLength - relative:[gc getWidth] - fontSize:fontSize]; - if (author < 0) { - NSException *e = [NSException - exceptionWithName:@"NegativeTextLength" - reason:@"Negative textLength value" - userInfo:nil]; - @throw e; - } - switch (mLengthAdjust) { - default: - case RNSVGTextLengthAdjustSpacing: - // TODO account for ligatures - letterSpacing += (author - textMeasure) / (n - 1); - break; - case RNSVGTextLengthAdjustSpacingAndGlyphs: - scaleSpacingAndGlyphs = author / textMeasure; - break; - } - } - CGFloat scaledDirection = scaleSpacingAndGlyphs * side; - - /* - https://developer.mozilla.org/en/docs/Web/CSS/vertical-align - https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6bsln.html - https://www.microsoft.com/typography/otspec/base.htm - http://apike.ca/prog_svg_text_style.html - https://www.w3schools.com/tags/canvas_textbaseline.asp - http://vanseodesign.com/web-design/svg-text-baseline-alignment/ - https://iamvdo.me/en/blog/css-font-metrics-line-height-and-vertical-align - https://tympanus.net/codrops/css_reference/vertical-align/ - - https://svgwg.org/svg2-draft/text.html#AlignmentBaselineProperty - 11.10.2.6. The ‘alignment-baseline’ property - - This property is defined in the CSS Line Layout Module 3 specification. See 'alignment-baseline'. [css-inline-3] - https://drafts.csswg.org/css-inline/#propdef-alignment-baseline - - The vertical-align property shorthand should be preferred in new content. - - SVG 2 introduces some changes to the definition of this property. - In particular: the values 'auto', 'before-edge', and 'after-edge' have been removed. - For backwards compatibility, 'text-before-edge' should be mapped to 'text-top' and - 'text-after-edge' should be mapped to 'text-bottom'. - - Neither 'text-before-edge' nor 'text-after-edge' should be used with the vertical-align property. - */ - /* - CGRect fontBounds = CTFontGetBoundingBox(fontRef); - CGFloat textHeight = CGRectGetHeight(textBounds); - CGFloat fontWidth = CGRectGetWidth(textBounds); - CGPoint fontOrigin = fontBounds.origin; - - CGFloat fontMinX = fontOrigin.x; - CGFloat fontMinY = fontOrigin.y; - CGFloat fontMaxX = fontMinX + fontWidth; - CGFloat fontMaxY = fontMinY + textHeight; - */ - // TODO - CGFloat descenderDepth = CTFontGetDescent(fontRef); - CGFloat bottom = descenderDepth + CTFontGetLeading(fontRef); - CGFloat ascenderHeight = CTFontGetAscent(fontRef); - CGFloat top = ascenderHeight; - CGFloat totalHeight = top + bottom; - CGFloat baselineShift = 0; - NSString *baselineShiftString = self.baselineShift; - enum RNSVGAlignmentBaseline baseline = RNSVGAlignmentBaselineFromString(self.alignmentBaseline); - if (baseline != RNSVGAlignmentBaselineBaseline) { - // TODO alignment-baseline, test / verify behavior - // TODO get per glyph baselines from font baseline table, for high-precision alignment - CGFloat xHeight = CTFontGetXHeight(fontRef); - switch (baseline) { - // https://wiki.apache.org/xmlgraphics-fop/LineLayout/AlignmentHandling - default: - case RNSVGAlignmentBaselineBaseline: - // Use the dominant baseline choice of the parent. - // Match the box’s corresponding baseline to that of its parent. - baselineShift = 0; - break; - - case RNSVGAlignmentBaselineTextBottom: - case RNSVGAlignmentBaselineAfterEdge: - case RNSVGAlignmentBaselineTextAfterEdge: - // Match the bottom of the box to the bottom of the parent’s content area. - // text-after-edge = text-bottom - // text-after-edge = descender depth - baselineShift = -descenderDepth; - break; - - case RNSVGAlignmentBaselineAlphabetic: - // Match the box’s alphabetic baseline to that of its parent. - // alphabetic = 0 - baselineShift = 0; - break; - - case RNSVGAlignmentBaselineIdeographic: - // Match the box’s ideographic character face under-side baseline to that of its parent. - // ideographic = descender depth - baselineShift = -descenderDepth; - break; - - case RNSVGAlignmentBaselineMiddle: - // Align the vertical midpoint of the box with the baseline of the parent box plus half the x-height of the parent. TODO - // middle = x height / 2 - baselineShift = xHeight / 2; - break; - - case RNSVGAlignmentBaselineCentral: - // Match the box’s central baseline to the central baseline of its parent. - // central = (ascender height - descender depth) / 2 - baselineShift = (ascenderHeight - descenderDepth) / 2; - break; - - case RNSVGAlignmentBaselineMathematical: - // Match the box’s mathematical baseline to that of its parent. - // Hanging and mathematical baselines - // There are no obvious formulas to calculate the position of these baselines. - // At the time of writing FOP puts the hanging baseline at 80% of the ascender - // height and the mathematical baseline at 50%. - baselineShift = (CGFloat)0.5 * ascenderHeight; - break; - - case RNSVGAlignmentBaselineHanging: - baselineShift = (CGFloat)0.8 * ascenderHeight; - break; - - case RNSVGAlignmentBaselineTextTop: - case RNSVGAlignmentBaselineBeforeEdge: - case RNSVGAlignmentBaselineTextBeforeEdge: - // Match the top of the box to the top of the parent’s content area. - // text-before-edge = text-top - // text-before-edge = ascender height - baselineShift = ascenderHeight; - break; - - case RNSVGAlignmentBaselineBottom: - // Align the top of the aligned subtree with the top of the line box. - baselineShift = bottom; - break; - - case RNSVGAlignmentBaselineCenter: - // Align the center of the aligned subtree with the center of the line box. - baselineShift = totalHeight / 2; - break; - - case RNSVGAlignmentBaselineTop: - // Align the bottom of the aligned subtree with the bottom of the line box. - baselineShift = top; - break; - } + CGFloat absoluteStartOffset = [RNSVGPropHelper fromRelative:textPath.startOffset + relative:measure.pathLength + fontSize:fontSize]; + offset += absoluteStartOffset; + if (measure.isClosed) { + CGFloat halfPathDistance = measure.pathLength / 2; + startOfRendering = absoluteStartOffset + (textAnchor == RNSVGTextAnchorMiddle ? -halfPathDistance : 0); + endOfRendering = startOfRendering + measure.pathLength; } /* - 2.2.2. Alignment Shift: baseline-shift longhand - - This property specifies by how much the box is shifted up from its alignment point. - It does not apply when alignment-baseline is top or bottom. - - Authors should use the vertical-align shorthand instead of this property. - - Values have the following meanings: - - - Raise (positive value) or lower (negative value) by the specified length. - - Raise (positive value) or lower (negative value) by the specified percentage of the line-height. - TODO sub - Lower by the offset appropriate for subscripts of the parent’s box. - (The UA should use the parent’s font data to find this offset whenever possible.) - TODO super - Raise by the offset appropriate for superscripts of the parent’s box. - (The UA should use the parent’s font data to find this offset whenever possible.) - - User agents may additionally support the keyword baseline as computing to 0 - if is necessary for them to support legacy SVG content. - Issue: We would prefer to remove this, - and are looking for feedback from SVG user agents as to whether it’s necessary. - - https://www.w3.org/TR/css-inline-3/#propdef-baseline-shift + RNSVGTextPathSpacing spacing = textPath.getSpacing(); + if (spacing == RNSVGTextPathSpacing.auto) { + // Hmm, what to do here? + // https://svgwg.org/svg2-draft/text.html#TextPathElementSpacingAttribute + } */ - if (baselineShiftString != nil && ![baselineShiftString isEqualToString:@""]) { - switch (baseline) { - case RNSVGAlignmentBaselineTop: - case RNSVGAlignmentBaselineBottom: - break; + } - default: - if (fontData != nil && [baselineShiftString isEqualToString:@"sub"]) { - // TODO - NSDictionary* tables = [fontData objectForKey:@"tables"]; - NSNumber* unitsPerEm = [fontData objectForKey:@"unitsPerEm"]; - NSDictionary* os2 = [tables objectForKey:@"os2"]; - NSNumber* ySubscriptYOffset = [os2 objectForKey:@"ySubscriptYOffset"]; - if (ySubscriptYOffset) { - CGFloat subOffset = (CGFloat)[ySubscriptYOffset doubleValue]; - baselineShift += fontSize * subOffset / [unitsPerEm doubleValue]; - } - } else if (fontData != nil && [baselineShiftString isEqualToString:@"super"]) { - // TODO - NSDictionary* tables = [fontData objectForKey:@"tables"]; - NSNumber* unitsPerEm = [fontData objectForKey:@"unitsPerEm"]; - NSDictionary* os2 = [tables objectForKey:@"os2"]; - NSNumber* ySuperscriptYOffset = [os2 objectForKey:@"ySuperscriptYOffset"]; - if (ySuperscriptYOffset) { - CGFloat superOffset = (CGFloat)[ySuperscriptYOffset doubleValue]; - baselineShift -= fontSize * superOffset / [unitsPerEm doubleValue]; - } - } else if ([baselineShiftString isEqualToString:@"baseline"]) { - } else { - baselineShift -= [RNSVGPropHelper fromRelativeWithNSString:baselineShiftString - relative:fontSize - fontSize:fontSize]; - } - break; + /* + Name + method + Value + align | stretch + initial value + align + Animatable + yes + Indicates the method by which text should be rendered along the path. + + A value of align indicates that the typographic character should be rendered using + simple 2×3 matrix transformations such that there is no stretching/warping of the + typographic characters. Typically, supplemental rotation, scaling and translation + transformations are done for each typographic characters to be rendered. + + As a result, with align, in fonts where the typographic characters are designed to be + connected (e.g., cursive fonts), the connections may not align properly when text is + rendered along a path. + + A value of stretch indicates that the typographic character outlines will be converted + into paths, and then all end points and control points will be adjusted to be along the + perpendicular vectors from the path, thereby stretching and possibly warping the glyphs. + + With this approach, connected typographic characters, such as in cursive scripts, + will maintain their connections. (Non-vertical straight path segments should be + converted to Bézier curves in such a way that horizontal straight paths have an + (approximately) constant offset from the path along which the typographic characters + are rendered.) + + TODO implement stretch + */ + + /* + Name Value Initial value Animatable + textLength | | See below yes + + The author's computation of the total sum of all of the advance values that correspond + to character data within this element, including the advance value on the glyph + (horizontal or vertical), the effect of properties letter-spacing and word-spacing and + adjustments due to attributes ‘dx’ and ‘dy’ on this ‘text’ or ‘tspan’ element or any + descendants. This value is used to calibrate the user agent's own calculations with + that of the author. + + The purpose of this attribute is to allow the author to achieve exact alignment, + in visual rendering order after any bidirectional reordering, for the first and + last rendered glyphs that correspond to this element; thus, for the last rendered + character (in visual rendering order after any bidirectional reordering), + any supplemental inter-character spacing beyond normal glyph advances are ignored + (in most cases) when the user agent determines the appropriate amount to expand/compress + the text string to fit within a length of ‘textLength’. + + If attribute ‘textLength’ is specified on a given element and also specified on an + ancestor, the adjustments on all character data within this element are controlled by + the value of ‘textLength’ on this element exclusively, with the possible side-effect + that the adjustment ratio for the contents of this element might be different than the + adjustment ratio used for other content that shares the same ancestor. The user agent + must assume that the total advance values for the other content within that ancestor is + the difference between the advance value on that ancestor and the advance value for + this element. + + This attribute is not intended for use to obtain effects such as shrinking or + expanding text. + + A negative value is an error (see Error processing). + + The ‘textLength’ attribute is only applied when the wrapping area is not defined by the + TODO shape-inside or the inline-size properties. It is also not applied for any ‘text’ or + TODO ‘tspan’ element that has forced line breaks (due to a white-space value of pre or + pre-line). + + If the attribute is not specified anywhere within a ‘text’ element, the effect is as if + the author's computation exactly matched the value calculated by the user agent; + thus, no advance adjustments are made. + */ + CGFloat scaleSpacingAndGlyphs = 1; + RNSVGLength *mTextLength = [self textLength]; + enum RNSVGTextLengthAdjust mLengthAdjust = RNSVGTextLengthAdjustFromString([self lengthAdjust]); + if (mTextLength != nil) { + CGFloat author = [RNSVGPropHelper fromRelative:mTextLength relative:[gc getWidth] fontSize:fontSize]; + if (author < 0) { + NSException *e = [NSException exceptionWithName:@"NegativeTextLength" + reason:@"Negative textLength value" + userInfo:nil]; + @throw e; + } + switch (mLengthAdjust) { + default: + case RNSVGTextLengthAdjustSpacing: + // TODO account for ligatures + letterSpacing += (author - textMeasure) / (n - 1); + break; + case RNSVGTextLengthAdjustSpacingAndGlyphs: + scaleSpacingAndGlyphs = author / textMeasure; + break; + } + } + CGFloat scaledDirection = scaleSpacingAndGlyphs * side; + + /* + https://developer.mozilla.org/en/docs/Web/CSS/vertical-align + https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6bsln.html + https://www.microsoft.com/typography/otspec/base.htm + http://apike.ca/prog_svg_text_style.html + https://www.w3schools.com/tags/canvas_textbaseline.asp + http://vanseodesign.com/web-design/svg-text-baseline-alignment/ + https://iamvdo.me/en/blog/css-font-metrics-line-height-and-vertical-align + https://tympanus.net/codrops/css_reference/vertical-align/ + + https://svgwg.org/svg2-draft/text.html#AlignmentBaselineProperty + 11.10.2.6. The ‘alignment-baseline’ property + + This property is defined in the CSS Line Layout Module 3 specification. See 'alignment-baseline'. [css-inline-3] + https://drafts.csswg.org/css-inline/#propdef-alignment-baseline + + The vertical-align property shorthand should be preferred in new content. + + SVG 2 introduces some changes to the definition of this property. + In particular: the values 'auto', 'before-edge', and 'after-edge' have been removed. + For backwards compatibility, 'text-before-edge' should be mapped to 'text-top' and + 'text-after-edge' should be mapped to 'text-bottom'. + + Neither 'text-before-edge' nor 'text-after-edge' should be used with the vertical-align property. + */ + /* + CGRect fontBounds = CTFontGetBoundingBox(fontRef); + CGFloat textHeight = CGRectGetHeight(textBounds); + CGFloat fontWidth = CGRectGetWidth(textBounds); + CGPoint fontOrigin = fontBounds.origin; + + CGFloat fontMinX = fontOrigin.x; + CGFloat fontMinY = fontOrigin.y; + CGFloat fontMaxX = fontMinX + fontWidth; + CGFloat fontMaxY = fontMinY + textHeight; + */ + // TODO + CGFloat descenderDepth = CTFontGetDescent(fontRef); + CGFloat bottom = descenderDepth + CTFontGetLeading(fontRef); + CGFloat ascenderHeight = CTFontGetAscent(fontRef); + CGFloat top = ascenderHeight; + CGFloat totalHeight = top + bottom; + CGFloat baselineShift = 0; + NSString *baselineShiftString = self.baselineShift; + enum RNSVGAlignmentBaseline baseline = RNSVGAlignmentBaselineFromString(self.alignmentBaseline); + if (baseline != RNSVGAlignmentBaselineBaseline) { + // TODO alignment-baseline, test / verify behavior + // TODO get per glyph baselines from font baseline table, for high-precision alignment + CGFloat xHeight = CTFontGetXHeight(fontRef); + switch (baseline) { + // https://wiki.apache.org/xmlgraphics-fop/LineLayout/AlignmentHandling + default: + case RNSVGAlignmentBaselineBaseline: + // Use the dominant baseline choice of the parent. + // Match the box’s corresponding baseline to that of its parent. + baselineShift = 0; + break; + + case RNSVGAlignmentBaselineTextBottom: + case RNSVGAlignmentBaselineAfterEdge: + case RNSVGAlignmentBaselineTextAfterEdge: + // Match the bottom of the box to the bottom of the parent’s content area. + // text-after-edge = text-bottom + // text-after-edge = descender depth + baselineShift = -descenderDepth; + break; + + case RNSVGAlignmentBaselineAlphabetic: + // Match the box’s alphabetic baseline to that of its parent. + // alphabetic = 0 + baselineShift = 0; + break; + + case RNSVGAlignmentBaselineIdeographic: + // Match the box’s ideographic character face under-side baseline to that of its parent. + // ideographic = descender depth + baselineShift = -descenderDepth; + break; + + case RNSVGAlignmentBaselineMiddle: + // Align the vertical midpoint of the box with the baseline of the parent box plus half the x-height of the + // parent. TODO middle = x height / 2 + baselineShift = xHeight / 2; + break; + + case RNSVGAlignmentBaselineCentral: + // Match the box’s central baseline to the central baseline of its parent. + // central = (ascender height - descender depth) / 2 + baselineShift = (ascenderHeight - descenderDepth) / 2; + break; + + case RNSVGAlignmentBaselineMathematical: + // Match the box’s mathematical baseline to that of its parent. + // Hanging and mathematical baselines + // There are no obvious formulas to calculate the position of these baselines. + // At the time of writing FOP puts the hanging baseline at 80% of the ascender + // height and the mathematical baseline at 50%. + baselineShift = (CGFloat)0.5 * ascenderHeight; + break; + + case RNSVGAlignmentBaselineHanging: + baselineShift = (CGFloat)0.8 * ascenderHeight; + break; + + case RNSVGAlignmentBaselineTextTop: + case RNSVGAlignmentBaselineBeforeEdge: + case RNSVGAlignmentBaselineTextBeforeEdge: + // Match the top of the box to the top of the parent’s content area. + // text-before-edge = text-top + // text-before-edge = ascender height + baselineShift = ascenderHeight; + break; + + case RNSVGAlignmentBaselineBottom: + // Align the top of the aligned subtree with the top of the line box. + baselineShift = bottom; + break; + + case RNSVGAlignmentBaselineCenter: + // Align the center of the aligned subtree with the center of the line box. + baselineShift = totalHeight / 2; + break; + + case RNSVGAlignmentBaselineTop: + // Align the bottom of the aligned subtree with the bottom of the line box. + baselineShift = top; + break; + } + } + /* + 2.2.2. Alignment Shift: baseline-shift longhand + + This property specifies by how much the box is shifted up from its alignment point. + It does not apply when alignment-baseline is top or bottom. + + Authors should use the vertical-align shorthand instead of this property. + + Values have the following meanings: + + + Raise (positive value) or lower (negative value) by the specified length. + + Raise (positive value) or lower (negative value) by the specified percentage of the line-height. + TODO sub + Lower by the offset appropriate for subscripts of the parent’s box. + (The UA should use the parent’s font data to find this offset whenever possible.) + TODO super + Raise by the offset appropriate for superscripts of the parent’s box. + (The UA should use the parent’s font data to find this offset whenever possible.) + + User agents may additionally support the keyword baseline as computing to 0 + if is necessary for them to support legacy SVG content. + Issue: We would prefer to remove this, + and are looking for feedback from SVG user agents as to whether it’s necessary. + + https://www.w3.org/TR/css-inline-3/#propdef-baseline-shift + */ + if (baselineShiftString != nil && ![baselineShiftString isEqualToString:@""]) { + switch (baseline) { + case RNSVGAlignmentBaselineTop: + case RNSVGAlignmentBaselineBottom: + break; + + default: + if (fontData != nil && [baselineShiftString isEqualToString:@"sub"]) { + // TODO + NSDictionary *tables = [fontData objectForKey:@"tables"]; + NSNumber *unitsPerEm = [fontData objectForKey:@"unitsPerEm"]; + NSDictionary *os2 = [tables objectForKey:@"os2"]; + NSNumber *ySubscriptYOffset = [os2 objectForKey:@"ySubscriptYOffset"]; + if (ySubscriptYOffset) { + CGFloat subOffset = (CGFloat)[ySubscriptYOffset doubleValue]; + baselineShift += fontSize * subOffset / [unitsPerEm doubleValue]; + } + } else if (fontData != nil && [baselineShiftString isEqualToString:@"super"]) { + // TODO + NSDictionary *tables = [fontData objectForKey:@"tables"]; + NSNumber *unitsPerEm = [fontData objectForKey:@"unitsPerEm"]; + NSDictionary *os2 = [tables objectForKey:@"os2"]; + NSNumber *ySuperscriptYOffset = [os2 objectForKey:@"ySuperscriptYOffset"]; + if (ySuperscriptYOffset) { + CGFloat superOffset = (CGFloat)[ySuperscriptYOffset doubleValue]; + baselineShift -= fontSize * superOffset / [unitsPerEm doubleValue]; + } + } else if ([baselineShiftString isEqualToString:@"baseline"]) { + } else { + baselineShift -= [RNSVGPropHelper fromRelativeWithNSString:baselineShiftString + relative:fontSize + fontSize:fontSize]; } + break; + } + } + + [emoji removeAllObjects]; + [emojiTransform removeAllObjects]; + + CFArrayRef runs = CTLineGetGlyphRuns(line); + CFIndex runEnd = CFArrayGetCount(runs); + for (CFIndex ri = 0; ri < runEnd; ri++) { + CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, ri); + CFIndex runGlyphCount = CTRunGetGlyphCount(run); + CFIndex indices[runGlyphCount]; + CGSize advances[runGlyphCount]; + CGGlyph glyphs[runGlyphCount]; + + // Grab the glyphs and font + CTRunGetGlyphs(run, CFRangeMake(0, 0), glyphs); + CTRunGetStringIndices(run, CFRangeMake(0, 0), indices); + CTFontRef runFont = (CTFontRef)CFDictionaryGetValue(CTRunGetAttributes(run), kCTFontAttributeName); + CTFontGetAdvancesForGlyphs(runFont, kCTFontOrientationHorizontal, glyphs, advances, runGlyphCount); + CFIndex nextOrEndRunIndex = n; + if (ri + 1 < runEnd) { + CTRunRef nextRun = (CTRunRef)CFArrayGetValueAtIndex(runs, ri + 1); + CFIndex nextRunGlyphCount = CTRunGetGlyphCount(nextRun); + CFIndex nextIndices[nextRunGlyphCount]; + CTRunGetStringIndices(nextRun, CFRangeMake(0, 0), nextIndices); + nextOrEndRunIndex = nextIndices[0]; } - [emoji removeAllObjects]; - [emojiTransform removeAllObjects]; + for (CFIndex g = 0; g < runGlyphCount; g++) { + CGGlyph glyph = glyphs[g]; - CFArrayRef runs = CTLineGetGlyphRuns(line); - CFIndex runEnd = CFArrayGetCount(runs); - for (CFIndex ri = 0; ri < runEnd; ri++) { - CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, ri); - CFIndex runGlyphCount = CTRunGetGlyphCount(run); - CFIndex indices[runGlyphCount]; - CGSize advances[runGlyphCount]; - CGGlyph glyphs[runGlyphCount]; + /* + Determine the glyph's charwidth (i.e., the amount which the current text position + advances horizontally when the glyph is drawn using horizontal text layout). - // Grab the glyphs and font - CTRunGetGlyphs(run, CFRangeMake(0, 0), glyphs); - CTRunGetStringIndices(run, CFRangeMake(0, 0), indices); - CTFontRef runFont = (CTFontRef)CFDictionaryGetValue(CTRunGetAttributes(run), kCTFontAttributeName); - CTFontGetAdvancesForGlyphs(runFont, kCTFontOrientationHorizontal, glyphs, advances, runGlyphCount); - CFIndex nextOrEndRunIndex = n; - if (ri + 1 < runEnd) { - CTRunRef nextRun = (CTRunRef)CFArrayGetValueAtIndex(runs, ri + 1); - CFIndex nextRunGlyphCount = CTRunGetGlyphCount(nextRun); - CFIndex nextIndices[nextRunGlyphCount]; - CTRunGetStringIndices(nextRun, CFRangeMake(0, 0), nextIndices); - nextOrEndRunIndex = nextIndices[0]; + For each subsequent glyph, set a new startpoint-on-the-path as the previous + endpoint-on-the-path, but with appropriate adjustments taking into account + horizontal kerning tables in the font and current values of various attributes + and properties, including spacing properties (e.g. letter-spacing and word-spacing) + and ‘tspan’ elements with values provided for attributes ‘dx’ and ‘dy’. All + adjustments are calculated as distance adjustments along the path, calculated + using the user agent's distance along the path algorithm. + */ + CGFloat charWidth = advances[g].width * scaleSpacingAndGlyphs; + + CFIndex currIndex = indices[g]; + unichar currentChar = [str characterAtIndex:currIndex]; + bool isWordSeparator = [RNSVGTSpan_separators characterIsMember:currentChar]; + CGFloat wordSpace = isWordSeparator ? wordSpacing : 0; + CGFloat spacing = wordSpace + letterSpacing; + CGFloat advance = charWidth + spacing; + + CGFloat x = [gc nextXWithDouble:kerning + advance]; + CGFloat y = [gc nextY]; + CGFloat dx = [gc nextDeltaX]; + CGFloat dy = [gc nextDeltaY]; + CGFloat r = [gc nextRotation] / RNSVGTSpan_radToDeg; + + if (isWordSeparator) { + continue; + } + + CFIndex endIndex = g + 1 == runGlyphCount ? nextOrEndRunIndex : indices[g + 1]; + while (++currIndex < endIndex) { + // Skip rendering other grapheme clusters of ligatures (already rendered), + // And, make sure to increment index positions by making gc.next() calls. + [gc nextXWithDouble:0]; + [gc nextY]; + [gc nextDeltaX]; + [gc nextDeltaY]; + [gc nextRotation]; + } + CGPathRef glyphPath = CTFontCreatePathForGlyph(runFont, glyph, nil); + + advance *= side; + charWidth *= side; + CGFloat cursor = offset + (x + dx) * side; + CGFloat startPoint = cursor - advance; + + CGAffineTransform transform = CGAffineTransformIdentity; + if (hasTextPath) { + /* + Determine the point on the curve which is charwidth distance along the path from + the startpoint-on-the-path for this glyph, calculated using the user agent's + distance along the path algorithm. This point is the endpoint-on-the-path for + the glyph. + */ + // TODO CGFloat endPoint = startPoint + charWidth; + + /* + Determine the midpoint-on-the-path, which is the point on the path which is + "halfway" (user agents can choose either a distance calculation or a parametric + calculation) between the startpoint-on-the-path and the endpoint-on-the-path. + */ + CGFloat halfWay = charWidth / 2; + CGFloat midPoint = startPoint + halfWay; + + // Glyphs whose midpoint-on-the-path are off the path are not rendered. + if (midPoint > endOfRendering) { + CGPathRelease(glyphPath); + continue; + } else if (midPoint < startOfRendering) { + CGPathRelease(glyphPath); + continue; } - for(CFIndex g = 0; g < runGlyphCount; g++) { - CGGlyph glyph = glyphs[g]; + CGFloat angle; + CGFloat px; + CGFloat py; + [measure getPosAndTan:&angle midPoint:midPoint x:&px y:&py]; - /* - Determine the glyph's charwidth (i.e., the amount which the current text position - advances horizontally when the glyph is drawn using horizontal text layout). + transform = CGAffineTransformConcat(CGAffineTransformMakeTranslation(px, py), transform); + transform = CGAffineTransformConcat(CGAffineTransformMakeRotation(angle + r), transform); + transform = CGAffineTransformScale(transform, scaledDirection, side); + transform = + CGAffineTransformConcat(CGAffineTransformMakeTranslation(-halfWay, y + dy + baselineShift), transform); + } else { + transform = CGAffineTransformMakeTranslation(startPoint, y + dy + baselineShift); + transform = CGAffineTransformConcat(CGAffineTransformMakeRotation(r), transform); + } - For each subsequent glyph, set a new startpoint-on-the-path as the previous - endpoint-on-the-path, but with appropriate adjustments taking into account - horizontal kerning tables in the font and current values of various attributes - and properties, including spacing properties (e.g. letter-spacing and word-spacing) - and ‘tspan’ elements with values provided for attributes ‘dx’ and ‘dy’. All - adjustments are calculated as distance adjustments along the path, calculated - using the user agent's distance along the path algorithm. - */ - CGFloat charWidth = advances[g].width * scaleSpacingAndGlyphs; + CGRect box = CGPathGetBoundingBox(glyphPath); + CGFloat width = box.size.width; - CFIndex currIndex = indices[g]; - unichar currentChar = [str characterAtIndex:currIndex]; - bool isWordSeparator = [RNSVGTSpan_separators characterIsMember:currentChar]; - CGFloat wordSpace = isWordSeparator ? wordSpacing : 0; - CGFloat spacing = wordSpace + letterSpacing; - CGFloat advance = charWidth + spacing; - - CGFloat x = [gc nextXWithDouble:kerning + advance]; - CGFloat y = [gc nextY]; - CGFloat dx = [gc nextDeltaX]; - CGFloat dy = [gc nextDeltaY]; - CGFloat r = [gc nextRotation] / RNSVGTSpan_radToDeg; - - if (isWordSeparator) { - continue; - } - - CFIndex endIndex = g + 1 == runGlyphCount ? nextOrEndRunIndex : indices[g + 1]; - while (++currIndex < endIndex) { - // Skip rendering other grapheme clusters of ligatures (already rendered), - // And, make sure to increment index positions by making gc.next() calls. - [gc nextXWithDouble:0]; - [gc nextY]; - [gc nextDeltaX]; - [gc nextDeltaY]; - [gc nextRotation]; - } - CGPathRef glyphPath = CTFontCreatePathForGlyph(runFont, glyph, nil); - - advance *= side; - charWidth *= side; - CGFloat cursor = offset + (x + dx) * side; - CGFloat startPoint = cursor - advance; - - CGAffineTransform transform = CGAffineTransformIdentity; - if (hasTextPath) { - /* - Determine the point on the curve which is charwidth distance along the path from - the startpoint-on-the-path for this glyph, calculated using the user agent's - distance along the path algorithm. This point is the endpoint-on-the-path for - the glyph. - */ - // TODO CGFloat endPoint = startPoint + charWidth; - - /* - Determine the midpoint-on-the-path, which is the point on the path which is - "halfway" (user agents can choose either a distance calculation or a parametric - calculation) between the startpoint-on-the-path and the endpoint-on-the-path. - */ - CGFloat halfWay = charWidth / 2; - CGFloat midPoint = startPoint + halfWay; - - // Glyphs whose midpoint-on-the-path are off the path are not rendered. - if (midPoint > endOfRendering) { - CGPathRelease(glyphPath); - continue; - } else if (midPoint < startOfRendering) { - CGPathRelease(glyphPath); - continue; - } - - CGFloat angle; - CGFloat px; - CGFloat py; - [measure getPosAndTan:&angle midPoint:midPoint x:&px y:&py]; - - transform = CGAffineTransformConcat(CGAffineTransformMakeTranslation(px, py), transform); - transform = CGAffineTransformConcat(CGAffineTransformMakeRotation(angle + r), transform); - transform = CGAffineTransformScale(transform, scaledDirection, side); - transform = CGAffineTransformConcat(CGAffineTransformMakeTranslation(-halfWay, y + dy + baselineShift), transform); - } else { - transform = CGAffineTransformMakeTranslation(startPoint, y + dy + baselineShift); - transform = CGAffineTransformConcat(CGAffineTransformMakeRotation(r), transform); - } - - CGRect box = CGPathGetBoundingBox(glyphPath); - CGFloat width = box.size.width; - - if (width == 0) { // Render unicode emoji - RNSVGTextView *label = [[RNSVGTextView alloc] init]; - CFIndex startIndex = indices[g]; - long len = MAX(1, endIndex - startIndex); - NSRange range = NSMakeRange(startIndex, len); - NSString* currChars = [str substringWithRange:range]; + if (width == 0) { // Render unicode emoji + RNSVGTextView *label = [[RNSVGTextView alloc] init]; + CFIndex startIndex = indices[g]; + long len = MAX(1, endIndex - startIndex); + NSRange range = NSMakeRange(startIndex, len); + NSString *currChars = [str substringWithRange:range]; #if TARGET_OS_OSX - label.string = currChars; + label.string = currChars; #else - label.text = currChars; - label.opaque = NO; + label.text = currChars; + label.opaque = NO; #endif // TARGET_OS_OSX - label.backgroundColor = RNSVGColor.clearColor; - UIFont * customFont = [UIFont systemFontOfSize:fontSize]; + label.backgroundColor = RNSVGColor.clearColor; + UIFont *customFont = [UIFont systemFontOfSize:fontSize]; - CGSize measuredSize = [currChars sizeWithAttributes: - @{NSFontAttributeName:customFont}]; - label.font = customFont; - CGFloat width = ceil(measuredSize.width); - CGFloat height = ceil(measuredSize.height); - CGRect bounds = CGRectMake(0, 0, width, height); - label.frame = bounds; + CGSize measuredSize = [currChars sizeWithAttributes:@{NSFontAttributeName : customFont}]; + label.font = customFont; + CGFloat width = ceil(measuredSize.width); + CGFloat height = ceil(measuredSize.height); + CGRect bounds = CGRectMake(0, 0, width, height); + label.frame = bounds; - CGContextConcatCTM(context, transform); - CGContextTranslateCTM(context, 0, -fontSize); - [label.layer renderInContext:context]; - CGContextTranslateCTM(context, 0, fontSize); - CGContextConcatCTM(context, CGAffineTransformInvert(transform)); + CGContextConcatCTM(context, transform); + CGContextTranslateCTM(context, 0, -fontSize); + [label.layer renderInContext:context]; + CGContextTranslateCTM(context, 0, fontSize); + CGContextConcatCTM(context, CGAffineTransformInvert(transform)); - [emoji addObject:label]; - [emojiTransform addObject:[NSValue valueWithCGAffineTransform:transform]]; - } else { - transform = CGAffineTransformScale(transform, 1.0, -1.0); - CGPathAddPath(path, &transform, glyphPath); - } - CGPathRelease(glyphPath); - } + [emoji addObject:label]; + [emojiTransform addObject:[NSValue valueWithCGAffineTransform:transform]]; + } else { + transform = CGAffineTransformScale(transform, 1.0, -1.0); + CGPathAddPath(path, &transform, glyphPath); + } + CGPathRelease(glyphPath); } + } - CFRelease(attrString); - CFRelease(line); + CFRelease(attrString); + CFRelease(line); - return (CGPathRef)CFAutorelease(path); + return (CGPathRef)CFAutorelease(path); } -+ (CGFloat)getTextAnchorOffset:(RNSVGTextAnchor)textAnchor width:(CGFloat) width ++ (CGFloat)getTextAnchorOffset:(RNSVGTextAnchor)textAnchor width:(CGFloat)width { - switch (textAnchor) { - case RNSVGTextAnchorStart: - return 0; - case RNSVGTextAnchorMiddle: - return -width / 2; - case RNSVGTextAnchorEnd: - return -width; - } + switch (textAnchor) { + case RNSVGTextAnchorStart: + return 0; + case RNSVGTextAnchorMiddle: + return -width / 2; + case RNSVGTextAnchorEnd: + return -width; + } - return 0; + return 0; } - (void)setupTextPath:(CGContextRef)context { - textPath = nil; - RNSVGText *parent = (RNSVGText*)[self superview]; - CGPathRef path = nil; - while (parent) { - if ([parent class] == [RNSVGTextPath class]) { - textPath = (RNSVGTextPath*) parent; - RNSVGNode *definedTemplate = [self.svgView getDefinedTemplate:textPath.href]; - path = [definedTemplate getPath:context]; - [measure extractPathData:path]; - break; - } else if (![parent isKindOfClass:[RNSVGText class]]) { - break; - } - parent = (RNSVGText*)[parent superview]; - } - if (!path) { - [measure reset]; + textPath = nil; + RNSVGText *parent = (RNSVGText *)[self superview]; + CGPathRef path = nil; + while (parent) { + if ([parent class] == [RNSVGTextPath class]) { + textPath = (RNSVGTextPath *)parent; + RNSVGNode *definedTemplate = [self.svgView getDefinedTemplate:textPath.href]; + path = [definedTemplate getPath:context]; + [measure extractPathData:path]; + break; + } else if (![parent isKindOfClass:[RNSVGText class]]) { + break; } + parent = (RNSVGText *)[parent superview]; + } + if (!path) { + [measure reset]; + } } @end diff --git a/apple/Text/RNSVGText.h b/apple/Text/RNSVGText.h index 71ec9465..be4fca7f 100644 --- a/apple/Text/RNSVGText.h +++ b/apple/Text/RNSVGText.h @@ -25,6 +25,6 @@ - (CGPathRef)getGroupPath:(CGContextRef)context; - (CTFontRef)getFontFromContext; - (CGFloat)getSubtreeTextChunksTotalAdvance; -- (RNSVGText*)getTextAnchorRoot; +- (RNSVGText *)getTextAnchorRoot; @end diff --git a/apple/Text/RNSVGText.mm b/apple/Text/RNSVGText.mm index 7173158d..0530aba2 100644 --- a/apple/Text/RNSVGText.mm +++ b/apple/Text/RNSVGText.mm @@ -7,26 +7,25 @@ */ #import "RNSVGText.h" -#import "RNSVGTextPath.h" -#import #import +#import #import "RNSVGGlyphContext.h" +#import "RNSVGTextPath.h" #import "RNSVGTextProperties.h" #ifdef RN_FABRIC_ENABLED #import -#import "RCTFabricComponentsPlugins.h" -#import "RCTConversions.h" #import +#import "RCTConversions.h" +#import "RCTFabricComponentsPlugins.h" #import "RNSVGFabricConversions.h" #endif // RN_FABRIC_ENABLED -@implementation RNSVGText -{ - RNSVGGlyphContext *_glyphContext; - NSString *_alignmentBaseline; - NSString *_baselineShift; - CGFloat cachedAdvance; +@implementation RNSVGText { + RNSVGGlyphContext *_glyphContext; + NSString *_alignmentBaseline; + NSString *_baselineShift; + CGFloat cachedAdvance; } #ifdef RN_FABRIC_ENABLED @@ -52,331 +51,329 @@ using namespace facebook::react; { const auto &newProps = *std::static_pointer_cast(props); - // textAnchor is in props of VM but not available on component - self.deltaX = createLengthArrayFromStrings(newProps.dx); - self.deltaY = createLengthArrayFromStrings(newProps.dy); - if (!newProps.positionX.empty()){ - self.positionX = createLengthArrayFromStrings(newProps.positionX); - } - if (!newProps.positionY.empty()){ - self.positionY = createLengthArrayFromStrings(newProps.positionY); - } - if (!newProps.x.empty()){ - self.positionX = createLengthArrayFromStrings(newProps.x); - } - if (!newProps.y.empty()){ - self.positionY = createLengthArrayFromStrings(newProps.y); - } - self.rotate = createLengthArrayFromStrings(newProps.rotate); - self.inlineSize = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.inlineSize)]; - self.textLength = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.textLength)]; - self.baselineShift = RCTNSStringFromStringNilIfEmpty(newProps.baselineShift); - self.lengthAdjust = RCTNSStringFromStringNilIfEmpty(newProps.lengthAdjust); - self.alignmentBaseline = RCTNSStringFromStringNilIfEmpty(newProps.alignmentBaseline); + // textAnchor is in props of VM but not available on component + self.deltaX = createLengthArrayFromStrings(newProps.dx); + self.deltaY = createLengthArrayFromStrings(newProps.dy); + if (!newProps.positionX.empty()) { + self.positionX = createLengthArrayFromStrings(newProps.positionX); + } + if (!newProps.positionY.empty()) { + self.positionY = createLengthArrayFromStrings(newProps.positionY); + } + if (!newProps.x.empty()) { + self.positionX = createLengthArrayFromStrings(newProps.x); + } + if (!newProps.y.empty()) { + self.positionY = createLengthArrayFromStrings(newProps.y); + } + self.rotate = createLengthArrayFromStrings(newProps.rotate); + self.inlineSize = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.inlineSize)]; + self.textLength = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.textLength)]; + self.baselineShift = RCTNSStringFromStringNilIfEmpty(newProps.baselineShift); + self.lengthAdjust = RCTNSStringFromStringNilIfEmpty(newProps.lengthAdjust); + self.alignmentBaseline = RCTNSStringFromStringNilIfEmpty(newProps.alignmentBaseline); - setCommonGroupProps(newProps, self); + setCommonGroupProps(newProps, self); } - (void)prepareForRecycle { - [super prepareForRecycle]; - - _deltaX = nil; - _deltaY = nil; - _positionX = nil; - _positionY = nil; - _rotate = nil; - _inlineSize = nil; - _textLength = nil; - _baselineShift = nil; - _lengthAdjust = nil; - _alignmentBaseline = nil; - - _glyphContext = nil; - _alignmentBaseline = nil; - _baselineShift = nil; - cachedAdvance = 0; + [super prepareForRecycle]; + + _deltaX = nil; + _deltaY = nil; + _positionX = nil; + _positionY = nil; + _rotate = nil; + _inlineSize = nil; + _textLength = nil; + _baselineShift = nil; + _lengthAdjust = nil; + _alignmentBaseline = nil; + + _glyphContext = nil; + _alignmentBaseline = nil; + _baselineShift = nil; + cachedAdvance = 0; } #endif // RN_FABRIC_ENABLED - (void)invalidate { - if (self.dirty || self.merging) { - return; - } - [super invalidate]; - [self clearChildCache]; + if (self.dirty || self.merging) { + return; + } + [super invalidate]; + [self clearChildCache]; } - (void)clearPath { - [super clearPath]; - cachedAdvance = NAN; + [super clearPath]; + cachedAdvance = NAN; } - (void)setInlineSize:(RNSVGLength *)inlineSize { - if ([inlineSize isEqualTo:_inlineSize]) { - return; - } - [self invalidate]; - _inlineSize = inlineSize; + if ([inlineSize isEqualTo:_inlineSize]) { + return; + } + [self invalidate]; + _inlineSize = inlineSize; } - (void)setTextLength:(RNSVGLength *)textLength { - if ([textLength isEqualTo:_textLength]) { - return; - } - [self invalidate]; - _textLength = textLength; + if ([textLength isEqualTo:_textLength]) { + return; + } + [self invalidate]; + _textLength = textLength; } - (void)setBaselineShift:(NSString *)baselineShift { - if ([baselineShift isEqualToString:_baselineShift]) { - return; - } - [self invalidate]; - _baselineShift = baselineShift; + if ([baselineShift isEqualToString:_baselineShift]) { + return; + } + [self invalidate]; + _baselineShift = baselineShift; } - (void)setLengthAdjust:(NSString *)lengthAdjust { - if ([lengthAdjust isEqualToString:_lengthAdjust]) { - return; - } - [self invalidate]; - _lengthAdjust = lengthAdjust; + if ([lengthAdjust isEqualToString:_lengthAdjust]) { + return; + } + [self invalidate]; + _lengthAdjust = lengthAdjust; } - (void)setAlignmentBaseline:(NSString *)alignmentBaseline { - if ([alignmentBaseline isEqualToString:_alignmentBaseline]) { - return; - } - [self invalidate]; - _alignmentBaseline = alignmentBaseline; + if ([alignmentBaseline isEqualToString:_alignmentBaseline]) { + return; + } + [self invalidate]; + _alignmentBaseline = alignmentBaseline; } - (void)setDeltaX:(NSArray *)deltaX { - if (deltaX == _deltaX) { - return; - } - [self invalidate]; - _deltaX = deltaX; + if (deltaX == _deltaX) { + return; + } + [self invalidate]; + _deltaX = deltaX; } - (void)setDeltaY:(NSArray *)deltaY { - if (deltaY == _deltaY) { - return; - } - [self invalidate]; - _deltaY = deltaY; + if (deltaY == _deltaY) { + return; + } + [self invalidate]; + _deltaY = deltaY; } -- (void)setPositionX:(NSArray*)positionX +- (void)setPositionX:(NSArray *)positionX { - if (positionX == _positionX) { - return; - } - [self invalidate]; - _positionX = positionX; + if (positionX == _positionX) { + return; + } + [self invalidate]; + _positionX = positionX; } -- (void)setPositionY:(NSArray*)positionY +- (void)setPositionY:(NSArray *)positionY { - if (positionY == _positionY) { - return; - } - [self invalidate]; - _positionY = positionY; + if (positionY == _positionY) { + return; + } + [self invalidate]; + _positionY = positionY; } - (void)setRotate:(NSArray *)rotate { - if (rotate == _rotate) { - return; - } - [self invalidate]; - _rotate = rotate; + if (rotate == _rotate) { + return; + } + [self invalidate]; + _rotate = rotate; } - (void)renderLayerTo:(CGContextRef)context rect:(CGRect)rect { - CGContextSaveGState(context); - [self clip:context]; - [self setupGlyphContext:context]; - [self pushGlyphContext]; - [super renderGroupTo:context rect:rect]; - [self popGlyphContext]; - CGContextRestoreGState(context); + CGContextSaveGState(context); + [self clip:context]; + [self setupGlyphContext:context]; + [self pushGlyphContext]; + [super renderGroupTo:context rect:rect]; + [self popGlyphContext]; + CGContextRestoreGState(context); } - (void)setupGlyphContext:(CGContextRef)context { - CGRect bounds = CGContextGetClipBoundingBox(context); - CGSize size = bounds.size; - _glyphContext = [[RNSVGGlyphContext alloc] initWithWidth:size.width - height:size.height]; + CGRect bounds = CGContextGetClipBoundingBox(context); + CGSize size = bounds.size; + _glyphContext = [[RNSVGGlyphContext alloc] initWithWidth:size.width height:size.height]; } - (CGPathRef)getGroupPath:(CGContextRef)context { - CGPathRef path = self.path; - if (path) { - return path; - } - [self pushGlyphContext]; - path = [super getPath:context]; - [self popGlyphContext]; - self.path = path; + CGPathRef path = self.path; + if (path) { return path; + } + [self pushGlyphContext]; + path = [super getPath:context]; + [self popGlyphContext]; + self.path = path; + return path; } - (CGPathRef)getPath:(CGContextRef)context { - CGPathRef path = self.path; - if (path) { - return path; - } - [self setupGlyphContext:context]; - return [self getGroupPath:context]; + CGPathRef path = self.path; + if (path) { + return path; + } + [self setupGlyphContext:context]; + return [self getGroupPath:context]; } - (void)renderGroupTo:(CGContextRef)context rect:(CGRect)rect { - [self pushGlyphContext]; - [super renderGroupTo:context rect:rect]; - [self popGlyphContext]; + [self pushGlyphContext]; + [super renderGroupTo:context rect:rect]; + [self popGlyphContext]; } // TODO: Optimisation required - (RNSVGText *)textRoot { - RNSVGText *root = self; - while (root && [root class] != [RNSVGText class]) { - if (![root isKindOfClass:[RNSVGText class]]) { - //todo: throw exception here - break; - } - root = (RNSVGText*)[root superview]; + RNSVGText *root = self; + while (root && [root class] != [RNSVGText class]) { + if (![root isKindOfClass:[RNSVGText class]]) { + // todo: throw exception here + break; } + root = (RNSVGText *)[root superview]; + } - return root; + return root; } - (NSString *)alignmentBaseline { - if (_alignmentBaseline != nil) { - return _alignmentBaseline; - } - - RNSVGPlatformView* parent = self.superview; - while (parent != nil) { - if ([parent isKindOfClass:[RNSVGText class]]) { - RNSVGText* node = (RNSVGText*)parent; - NSString* baseline = node.alignmentBaseline; - if (baseline != nil) { - _alignmentBaseline = baseline; - return baseline; - } - } - parent = [parent superview]; - } - - if (_alignmentBaseline == nil) { - _alignmentBaseline = RNSVGAlignmentBaselineStrings[0]; - } + if (_alignmentBaseline != nil) { return _alignmentBaseline; + } + + RNSVGPlatformView *parent = self.superview; + while (parent != nil) { + if ([parent isKindOfClass:[RNSVGText class]]) { + RNSVGText *node = (RNSVGText *)parent; + NSString *baseline = node.alignmentBaseline; + if (baseline != nil) { + _alignmentBaseline = baseline; + return baseline; + } + } + parent = [parent superview]; + } + + if (_alignmentBaseline == nil) { + _alignmentBaseline = RNSVGAlignmentBaselineStrings[0]; + } + return _alignmentBaseline; } - (NSString *)baselineShift { - if (_baselineShift != nil) { - return _baselineShift; - } - - RNSVGPlatformView* parent = [self superview]; - while (parent != nil) { - if ([parent isKindOfClass:[RNSVGText class]]) { - RNSVGText* node = (RNSVGText*)parent; - NSString* baselineShift = node.baselineShift; - if (baselineShift != nil) { - _baselineShift = baselineShift; - return baselineShift; - } - } - parent = [parent superview]; - } - - // set default value - _baselineShift = @""; - + if (_baselineShift != nil) { return _baselineShift; + } + + RNSVGPlatformView *parent = [self superview]; + while (parent != nil) { + if ([parent isKindOfClass:[RNSVGText class]]) { + RNSVGText *node = (RNSVGText *)parent; + NSString *baselineShift = node.baselineShift; + if (baselineShift != nil) { + _baselineShift = baselineShift; + return baselineShift; + } + } + parent = [parent superview]; + } + + // set default value + _baselineShift = @""; + + return _baselineShift; } - (RNSVGGlyphContext *)getGlyphContext { - return _glyphContext; + return _glyphContext; } - (void)pushGlyphContext { - [[self.textRoot getGlyphContext] pushContext:self - font:self.font - x:self.positionX - y:self.positionY - deltaX:self.deltaX - deltaY:self.deltaY - rotate:self.rotate]; + [[self.textRoot getGlyphContext] pushContext:self + font:self.font + x:self.positionX + y:self.positionY + deltaX:self.deltaX + deltaY:self.deltaY + rotate:self.rotate]; } - (void)popGlyphContext { - [[self.textRoot getGlyphContext] popContext]; + [[self.textRoot getGlyphContext] popContext]; } - (CTFontRef)getFontFromContext { - return [[self.textRoot getGlyphContext] getGlyphFont]; + return [[self.textRoot getGlyphContext] getGlyphFont]; } -- (RNSVGText*)getTextAnchorRoot +- (RNSVGText *)getTextAnchorRoot { - RNSVGGlyphContext* gc = [self.textRoot getGlyphContext]; - NSArray* font = [gc getFontContext]; - RNSVGText* node = self; - RNSVGPlatformView* parent = [self superview]; - for (NSInteger i = [font count] - 1; i >= 0; i--) { - RNSVGFontData* fontData = [font objectAtIndex:i]; - if (![parent isKindOfClass:[RNSVGText class]] || - fontData->textAnchor == RNSVGTextAnchorStart || - node.positionX != nil) { - return node; - } - node = (RNSVGText*) parent; - parent = [node superview]; + RNSVGGlyphContext *gc = [self.textRoot getGlyphContext]; + NSArray *font = [gc getFontContext]; + RNSVGText *node = self; + RNSVGPlatformView *parent = [self superview]; + for (NSInteger i = [font count] - 1; i >= 0; i--) { + RNSVGFontData *fontData = [font objectAtIndex:i]; + if (![parent isKindOfClass:[RNSVGText class]] || fontData->textAnchor == RNSVGTextAnchorStart || + node.positionX != nil) { + return node; } - return node; + node = (RNSVGText *)parent; + parent = [node superview]; + } + return node; } - (CGFloat)getSubtreeTextChunksTotalAdvance { - if (!isnan(cachedAdvance)) { - return cachedAdvance; + if (!isnan(cachedAdvance)) { + return cachedAdvance; + } + CGFloat advance = 0; + for (RNSVGView *node in self.subviews) { + if ([node isKindOfClass:[RNSVGText class]]) { + RNSVGText *text = (RNSVGText *)node; + advance += [text getSubtreeTextChunksTotalAdvance]; } - CGFloat advance = 0; - for (RNSVGView *node in self.subviews) { - if ([node isKindOfClass:[RNSVGText class]]) { - RNSVGText *text = (RNSVGText*)node; - advance += [text getSubtreeTextChunksTotalAdvance]; - } - } - cachedAdvance = advance; - return advance; + } + cachedAdvance = advance; + return advance; } @end diff --git a/apple/Text/RNSVGTextPath.h b/apple/Text/RNSVGTextPath.h index 820f575e..ada5e60b 100644 --- a/apple/Text/RNSVGTextPath.h +++ b/apple/Text/RNSVGTextPath.h @@ -6,10 +6,10 @@ * LICENSE file in the root directory of this source tree. */ -#import #import -#import "RNSVGText.h" +#import #import "RNSVGLength.h" +#import "RNSVGText.h" @interface RNSVGTextPath : RNSVGText diff --git a/apple/Text/RNSVGTextPath.mm b/apple/Text/RNSVGTextPath.mm index dabe1ac7..0dfbc855 100644 --- a/apple/Text/RNSVGTextPath.mm +++ b/apple/Text/RNSVGTextPath.mm @@ -6,14 +6,13 @@ * LICENSE file in the root directory of this source tree. */ - #import "RNSVGTextPath.h" #ifdef RN_FABRIC_ENABLED #import -#import "RCTFabricComponentsPlugins.h" -#import "RCTConversions.h" #import +#import "RCTConversions.h" +#import "RCTFabricComponentsPlugins.h" #import "RNSVGFabricConversions.h" #endif // RN_FABRIC_ENABLED @@ -42,101 +41,101 @@ using namespace facebook::react; { const auto &newProps = *std::static_pointer_cast(props); - self.href = RCTNSStringFromStringNilIfEmpty(newProps.href); - self.side = RCTNSStringFromStringNilIfEmpty(newProps.side); - self.method = RCTNSStringFromStringNilIfEmpty(newProps.method); - self.midLine = RCTNSStringFromStringNilIfEmpty(newProps.midLine); - self.spacing = RCTNSStringFromStringNilIfEmpty(newProps.spacing); - self.startOffset = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.startOffset)]; + self.href = RCTNSStringFromStringNilIfEmpty(newProps.href); + self.side = RCTNSStringFromStringNilIfEmpty(newProps.side); + self.method = RCTNSStringFromStringNilIfEmpty(newProps.method); + self.midLine = RCTNSStringFromStringNilIfEmpty(newProps.midLine); + self.spacing = RCTNSStringFromStringNilIfEmpty(newProps.spacing); + self.startOffset = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.startOffset)]; - setCommonRenderableProps(newProps, self); + setCommonRenderableProps(newProps, self); } - (void)prepareForRecycle { - [super prepareForRecycle]; - - _href = nil; - _side = nil; - _method = nil; - _midLine = nil; - _spacing = nil; - _startOffset = nil; + [super prepareForRecycle]; + + _href = nil; + _side = nil; + _method = nil; + _midLine = nil; + _spacing = nil; + _startOffset = nil; } #endif // RN_FABRIC_ENABLED - (void)setHref:(NSString *)href { - if ([href isEqualToString:_href]) { - return; - } - [self invalidate]; - _href = href; + if ([href isEqualToString:_href]) { + return; + } + [self invalidate]; + _href = href; } - (void)setSide:(NSString *)side { - if ([side isEqualToString:_side]) { - return; - } - [self invalidate]; - _side = side; + if ([side isEqualToString:_side]) { + return; + } + [self invalidate]; + _side = side; } - (void)setMethod:(NSString *)method { - if ([method isEqualToString:_method]) { - return; - } - [self invalidate]; - _method = method; + if ([method isEqualToString:_method]) { + return; + } + [self invalidate]; + _method = method; } - (void)setMidLine:(NSString *)midLine { - if ([midLine isEqualToString:_midLine]) { - return; - } - [self invalidate]; - _midLine = midLine; + if ([midLine isEqualToString:_midLine]) { + return; + } + [self invalidate]; + _midLine = midLine; } - (void)setSpacing:(NSString *)spacing { - if ([spacing isEqualToString:_spacing]) { - return; - } - [self invalidate]; - _spacing = spacing; + if ([spacing isEqualToString:_spacing]) { + return; + } + [self invalidate]; + _spacing = spacing; } - (void)setStartOffset:(RNSVGLength *)startOffset { - if ([startOffset isEqualTo:_startOffset]) { - return; - } - [self invalidate]; - _startOffset = startOffset; + if ([startOffset isEqualTo:_startOffset]) { + return; + } + [self invalidate]; + _startOffset = startOffset; } - (void)renderLayerTo:(CGContextRef)context rect:(CGRect)rect { - [self renderGroupTo:context rect:rect]; + [self renderGroupTo:context rect:rect]; } - (CGPathRef)getPath:(CGContextRef)context { - return [self getGroupPath:context]; + return [self getGroupPath:context]; } - (void)pushGlyphContext { - // TextPath do not affect the glyphContext + // TextPath do not affect the glyphContext } - (void)popGlyphContext { - // TextPath do not affect the glyphContext + // TextPath do not affect the glyphContext } @end diff --git a/apple/Text/RNSVGTextProperties.h b/apple/Text/RNSVGTextProperties.h index c48f70e6..c78bd365 100644 --- a/apple/Text/RNSVGTextProperties.h +++ b/apple/Text/RNSVGTextProperties.h @@ -4,107 +4,101 @@ #define RNTextProperties_h typedef NS_ENUM(NSInteger, RNSVGAlignmentBaseline) { - RNSVGAlignmentBaselineBaseline, - RNSVGAlignmentBaselineTextBottom, - RNSVGAlignmentBaselineAlphabetic, - RNSVGAlignmentBaselineIdeographic, - RNSVGAlignmentBaselineMiddle, - RNSVGAlignmentBaselineCentral, - RNSVGAlignmentBaselineMathematical, - RNSVGAlignmentBaselineTextTop, - RNSVGAlignmentBaselineBottom, - RNSVGAlignmentBaselineCenter, - RNSVGAlignmentBaselineTop, - /* - SVG implementations may support the following aliases in order to support legacy content: - - text-before-edge = text-top - text-after-edge = text-bottom - */ - RNSVGAlignmentBaselineTextBeforeEdge, - RNSVGAlignmentBaselineTextAfterEdge, - // SVG 1.1 - RNSVGAlignmentBaselineBeforeEdge, - RNSVGAlignmentBaselineAfterEdge, - RNSVGAlignmentBaselineHanging, - RNSVGAlignmentBaselineDEFAULT = RNSVGAlignmentBaselineBaseline + RNSVGAlignmentBaselineBaseline, + RNSVGAlignmentBaselineTextBottom, + RNSVGAlignmentBaselineAlphabetic, + RNSVGAlignmentBaselineIdeographic, + RNSVGAlignmentBaselineMiddle, + RNSVGAlignmentBaselineCentral, + RNSVGAlignmentBaselineMathematical, + RNSVGAlignmentBaselineTextTop, + RNSVGAlignmentBaselineBottom, + RNSVGAlignmentBaselineCenter, + RNSVGAlignmentBaselineTop, + /* + SVG implementations may support the following aliases in order to support legacy content: + + text-before-edge = text-top + text-after-edge = text-bottom + */ + RNSVGAlignmentBaselineTextBeforeEdge, + RNSVGAlignmentBaselineTextAfterEdge, + // SVG 1.1 + RNSVGAlignmentBaselineBeforeEdge, + RNSVGAlignmentBaselineAfterEdge, + RNSVGAlignmentBaselineHanging, + RNSVGAlignmentBaselineDEFAULT = RNSVGAlignmentBaselineBaseline }; -static NSString* const RNSVGAlignmentBaselineStrings[] = { - @"baseline", - @"text-bottom", - @"alphabetic", - @"ideographic", - @"middle", - @"central", - @"mathematical", - @"text-top", - @"bottom", - @"center", - @"top", - @"text-before-edge", - @"text-after-edge", - @"before-edge", - @"after-edge", - @"hanging", - @"central", - @"mathematical", - @"text-top", - @"bottom", - @"center", - @"top", - nil -}; +static NSString *const RNSVGAlignmentBaselineStrings[] = { + @"baseline", @"text-bottom", @"alphabetic", @"ideographic", @"middle", @"central", + @"mathematical", @"text-top", @"bottom", @"center", @"top", @"text-before-edge", + @"text-after-edge", @"before-edge", @"after-edge", @"hanging", @"central", @"mathematical", + @"text-top", @"bottom", @"center", @"top", nil}; -NSString* RNSVGAlignmentBaselineToString( enum RNSVGAlignmentBaseline fw ); +NSString *RNSVGAlignmentBaselineToString(enum RNSVGAlignmentBaseline fw); -enum RNSVGAlignmentBaseline RNSVGAlignmentBaselineFromString( NSString* s ); +enum RNSVGAlignmentBaseline RNSVGAlignmentBaselineFromString(NSString *s); typedef NS_ENUM(NSInteger, RNSVGFontStyle) { - RNSVGFontStyleNormal, - RNSVGFontStyleItalic, - RNSVGFontStyleOblique, - RNSVGFontStyleDEFAULT = RNSVGFontStyleNormal, + RNSVGFontStyleNormal, + RNSVGFontStyleItalic, + RNSVGFontStyleOblique, + RNSVGFontStyleDEFAULT = RNSVGFontStyleNormal, }; -static NSString* const RNSVGFontStyleStrings[] = {@"normal", @"italic", @"oblique", nil}; +static NSString *const RNSVGFontStyleStrings[] = {@"normal", @"italic", @"oblique", nil}; -NSString* RNSVGFontStyleToString( enum RNSVGFontStyle fw ); +NSString *RNSVGFontStyleToString(enum RNSVGFontStyle fw); -enum RNSVGFontStyle RNSVGFontStyleFromString( NSString* s ); +enum RNSVGFontStyle RNSVGFontStyleFromString(NSString *s); typedef NS_ENUM(NSInteger, RNSVGFontVariantLigatures) { - RNSVGFontVariantLigaturesNormal, - RNSVGFontVariantLigaturesNone, - RNSVGFontVariantLigaturesDEFAULT = RNSVGFontVariantLigaturesNormal, + RNSVGFontVariantLigaturesNormal, + RNSVGFontVariantLigaturesNone, + RNSVGFontVariantLigaturesDEFAULT = RNSVGFontVariantLigaturesNormal, }; -static NSString* const RNSVGFontVariantLigaturesStrings[] = {@"normal", @"none", nil}; +static NSString *const RNSVGFontVariantLigaturesStrings[] = {@"normal", @"none", nil}; -NSString* RNSVGFontVariantLigaturesToString( enum RNSVGFontVariantLigatures fw ); +NSString *RNSVGFontVariantLigaturesToString(enum RNSVGFontVariantLigatures fw); -enum RNSVGFontVariantLigatures RNSVGFontVariantLigaturesFromString( NSString* s ); +enum RNSVGFontVariantLigatures RNSVGFontVariantLigaturesFromString(NSString *s); typedef NS_ENUM(NSInteger, RNSVGFontWeight) { - // Absolute - RNSVGFontWeightNormal, - RNSVGFontWeightBold, - RNSVGFontWeight100, - RNSVGFontWeight200, - RNSVGFontWeight300, - RNSVGFontWeight400, - RNSVGFontWeight500, - RNSVGFontWeight600, - RNSVGFontWeight700, - RNSVGFontWeight800, - RNSVGFontWeight900, - // Relative - RNSVGFontWeightBolder, - RNSVGFontWeightLighter, - RNSVGFontWeightDEFAULT = RNSVGFontWeightNormal, + // Absolute + RNSVGFontWeightNormal, + RNSVGFontWeightBold, + RNSVGFontWeight100, + RNSVGFontWeight200, + RNSVGFontWeight300, + RNSVGFontWeight400, + RNSVGFontWeight500, + RNSVGFontWeight600, + RNSVGFontWeight700, + RNSVGFontWeight800, + RNSVGFontWeight900, + // Relative + RNSVGFontWeightBolder, + RNSVGFontWeightLighter, + RNSVGFontWeightDEFAULT = RNSVGFontWeightNormal, }; -static NSString* const RNSVGFontWeightStrings[] = {@"normal", @"bold", @"100", @"200", @"300", @"400", @"500", @"600", @"700", @"800", @"900", @"bolder", @"lighter", nil}; +static NSString *const RNSVGFontWeightStrings[] = { + @"normal", + @"bold", + @"100", + @"200", + @"300", + @"400", + @"500", + @"600", + @"700", + @"800", + @"900", + @"bolder", + @"lighter", + nil}; static int const RNSVGAbsoluteFontWeights[] = {400, 700, 100, 200, 300, 400, 500, 600, 700, 800, 900}; @@ -119,99 +113,99 @@ static RNSVGFontWeight const RNSVGFontWeights[] = { RNSVGFontWeightBold, RNSVGFontWeight800, RNSVGFontWeight900, - RNSVGFontWeight900 -}; + RNSVGFontWeight900}; -NSString* RNSVGFontWeightToString( enum RNSVGFontWeight fw ); +NSString *RNSVGFontWeightToString(enum RNSVGFontWeight fw); -NSInteger RNSVGFontWeightFromString( NSString* s ); +NSInteger RNSVGFontWeightFromString(NSString *s); typedef NS_ENUM(NSInteger, RNSVGTextAnchor) { - RNSVGTextAnchorStart, - RNSVGTextAnchorMiddle, - RNSVGTextAnchorEnd, - RNSVGTextAnchorDEFAULT = RNSVGTextAnchorStart, + RNSVGTextAnchorStart, + RNSVGTextAnchorMiddle, + RNSVGTextAnchorEnd, + RNSVGTextAnchorDEFAULT = RNSVGTextAnchorStart, }; -static NSString* const RNSVGTextAnchorStrings[] = {@"start", @"middle", @"end", nil}; +static NSString *const RNSVGTextAnchorStrings[] = {@"start", @"middle", @"end", nil}; -NSString* RNSVGTextAnchorToString( enum RNSVGTextAnchor fw ); +NSString *RNSVGTextAnchorToString(enum RNSVGTextAnchor fw); -enum RNSVGTextAnchor RNSVGTextAnchorFromString( NSString* s ); +enum RNSVGTextAnchor RNSVGTextAnchorFromString(NSString *s); typedef NS_ENUM(NSInteger, RNSVGTextDecoration) { - RNSVGTextDecorationNone, - RNSVGTextDecorationUnderline, - RNSVGTextDecorationOverline, - RNSVGTextDecorationLineThrough, - RNSVGTextDecorationBlink, - RNSVGTextDecorationDEFAULT = RNSVGTextDecorationNone, + RNSVGTextDecorationNone, + RNSVGTextDecorationUnderline, + RNSVGTextDecorationOverline, + RNSVGTextDecorationLineThrough, + RNSVGTextDecorationBlink, + RNSVGTextDecorationDEFAULT = RNSVGTextDecorationNone, }; -static NSString* const RNSVGTextDecorationStrings[] = {@"None", @"Underline", @"Overline", @"LineThrough", @"Blink", nil}; +static NSString *const RNSVGTextDecorationStrings[] = + {@"None", @"Underline", @"Overline", @"LineThrough", @"Blink", nil}; -NSString* RNSVGTextDecorationToString( enum RNSVGTextDecoration fw ); +NSString *RNSVGTextDecorationToString(enum RNSVGTextDecoration fw); -enum RNSVGTextDecoration RNSVGTextDecorationFromString( NSString* s ); +enum RNSVGTextDecoration RNSVGTextDecorationFromString(NSString *s); typedef NS_ENUM(NSInteger, RNSVGTextLengthAdjust) { - RNSVGTextLengthAdjustSpacing, - RNSVGTextLengthAdjustSpacingAndGlyphs, - RNSVGTextLengthAdjustDEFAULT = RNSVGTextLengthAdjustSpacing, + RNSVGTextLengthAdjustSpacing, + RNSVGTextLengthAdjustSpacingAndGlyphs, + RNSVGTextLengthAdjustDEFAULT = RNSVGTextLengthAdjustSpacing, }; -static NSString* const RNSVGTextLengthAdjustStrings[] = {@"spacing", @"spacingAndGlyphs", nil}; +static NSString *const RNSVGTextLengthAdjustStrings[] = {@"spacing", @"spacingAndGlyphs", nil}; -NSString* RNSVGTextLengthAdjustToString( enum RNSVGTextLengthAdjust fw ); +NSString *RNSVGTextLengthAdjustToString(enum RNSVGTextLengthAdjust fw); -enum RNSVGTextLengthAdjust RNSVGTextLengthAdjustFromString( NSString* s ); +enum RNSVGTextLengthAdjust RNSVGTextLengthAdjustFromString(NSString *s); typedef NS_ENUM(NSInteger, RNSVGTextPathMethod) { - RNSVGTextPathMethodAlign, - RNSVGTextPathMethodStretch, - RNSVGTextPathMethodDEFAULT = RNSVGTextPathMethodAlign, + RNSVGTextPathMethodAlign, + RNSVGTextPathMethodStretch, + RNSVGTextPathMethodDEFAULT = RNSVGTextPathMethodAlign, }; -static NSString* const RNSVGTextPathMethodStrings[] = {@"align", @"stretch", nil}; +static NSString *const RNSVGTextPathMethodStrings[] = {@"align", @"stretch", nil}; -NSString* RNSVGTextPathMethodToString( enum RNSVGTextPathMethod fw ); +NSString *RNSVGTextPathMethodToString(enum RNSVGTextPathMethod fw); -enum RNSVGTextPathMethod RNSVGTextPathMethodFromString( NSString* s ); +enum RNSVGTextPathMethod RNSVGTextPathMethodFromString(NSString *s); typedef NS_ENUM(NSInteger, RNSVGTextPathMidLine) { - RNSVGTextPathMidLineSharp, - RNSVGTextPathMidLineSmooth, - RNSVGTextPathMidLineDEFAULT = RNSVGTextPathMidLineSharp, + RNSVGTextPathMidLineSharp, + RNSVGTextPathMidLineSmooth, + RNSVGTextPathMidLineDEFAULT = RNSVGTextPathMidLineSharp, }; -static NSString* const RNSVGTextPathMidLineStrings[] = {@"sharp", @"smooth", nil}; +static NSString *const RNSVGTextPathMidLineStrings[] = {@"sharp", @"smooth", nil}; -NSString* RNSVGTextPathMidLineToString( enum RNSVGTextPathMidLine fw ); +NSString *RNSVGTextPathMidLineToString(enum RNSVGTextPathMidLine fw); -enum RNSVGTextPathMidLine RNSVGTextPathMidLineFromString( NSString* s ); +enum RNSVGTextPathMidLine RNSVGTextPathMidLineFromString(NSString *s); typedef NS_ENUM(NSInteger, RNSVGTextPathSide) { - RNSVGTextPathSideLeft, - RNSVGTextPathSideRight, - RNSVGTextPathSideDEFAULT = RNSVGTextPathSideLeft, + RNSVGTextPathSideLeft, + RNSVGTextPathSideRight, + RNSVGTextPathSideDEFAULT = RNSVGTextPathSideLeft, }; -static NSString* const RNSVGTextPathSideStrings[] = {@"left", @"right", nil}; +static NSString *const RNSVGTextPathSideStrings[] = {@"left", @"right", nil}; -NSString* RNSVGTextPathSideToString( enum RNSVGTextPathSide fw ); +NSString *RNSVGTextPathSideToString(enum RNSVGTextPathSide fw); -enum RNSVGTextPathSide RNSVGTextPathSideFromString( NSString* s ); +enum RNSVGTextPathSide RNSVGTextPathSideFromString(NSString *s); typedef NS_ENUM(NSInteger, RNSVGTextPathSpacing) { - RNSVGTextPathSpacingAutoSpacing, - RNSVGTextPathSpacingExact, - RNSVGTextPathSpacingDEFAULT = RNSVGTextPathSpacingAutoSpacing, + RNSVGTextPathSpacingAutoSpacing, + RNSVGTextPathSpacingExact, + RNSVGTextPathSpacingDEFAULT = RNSVGTextPathSpacingAutoSpacing, }; -static NSString* const RNSVGTextPathSpacingStrings[] = {@"auto", @"exact", nil}; +static NSString *const RNSVGTextPathSpacingStrings[] = {@"auto", @"exact", nil}; -NSString* RNSVGTextPathSpacingToString( enum RNSVGTextPathSpacing fw ); +NSString *RNSVGTextPathSpacingToString(enum RNSVGTextPathSpacing fw); -enum RNSVGTextPathSpacing RNSVGTextPathSpacingFromString( NSString* s ); +enum RNSVGTextPathSpacing RNSVGTextPathSpacingFromString(NSString *s); #endif diff --git a/apple/Text/RNSVGTextProperties.mm b/apple/Text/RNSVGTextProperties.mm index 02476c6e..ea5e750a 100644 --- a/apple/Text/RNSVGTextProperties.mm +++ b/apple/Text/RNSVGTextProperties.mm @@ -2,231 +2,231 @@ #pragma mark - RNSVGAlignmentBaseline -NSString* RNSVGAlignmentBaselineToString( enum RNSVGAlignmentBaseline fw ) +NSString *RNSVGAlignmentBaselineToString(enum RNSVGAlignmentBaseline fw) { - return RNSVGAlignmentBaselineStrings[fw]; + return RNSVGAlignmentBaselineStrings[fw]; } -enum RNSVGAlignmentBaseline RNSVGAlignmentBaselineFromString( NSString* s ) +enum RNSVGAlignmentBaseline RNSVGAlignmentBaselineFromString(NSString *s) { - if ([s length] == 0) { - return RNSVGAlignmentBaselineDEFAULT; - } - const NSUInteger l = sizeof(RNSVGAlignmentBaselineStrings) / sizeof(NSString*); - for (NSUInteger i = 0; i < l; i++) { - if ([s isEqualToString:RNSVGAlignmentBaselineStrings[i]]) { - return (RNSVGAlignmentBaseline)i; - } - } + if ([s length] == 0) { return RNSVGAlignmentBaselineDEFAULT; + } + const NSUInteger l = sizeof(RNSVGAlignmentBaselineStrings) / sizeof(NSString *); + for (NSUInteger i = 0; i < l; i++) { + if ([s isEqualToString:RNSVGAlignmentBaselineStrings[i]]) { + return (RNSVGAlignmentBaseline)i; + } + } + return RNSVGAlignmentBaselineDEFAULT; } #pragma mark - RNSVGFontStyle -NSString* RNSVGFontStyleToString( enum RNSVGFontStyle fw ) +NSString *RNSVGFontStyleToString(enum RNSVGFontStyle fw) { - return RNSVGFontStyleStrings[fw]; + return RNSVGFontStyleStrings[fw]; } -enum RNSVGFontStyle RNSVGFontStyleFromString( NSString* s ) +enum RNSVGFontStyle RNSVGFontStyleFromString(NSString *s) { - if ([s length] == 0) { - return RNSVGFontStyleDEFAULT; - } - const NSUInteger l = sizeof(RNSVGFontStyleStrings) / sizeof(NSString*); - for (NSUInteger i = 0; i < l; i++) { - if ([s isEqualToString:RNSVGFontStyleStrings[i]]) { - return (RNSVGFontStyle)i; - } - } + if ([s length] == 0) { return RNSVGFontStyleDEFAULT; + } + const NSUInteger l = sizeof(RNSVGFontStyleStrings) / sizeof(NSString *); + for (NSUInteger i = 0; i < l; i++) { + if ([s isEqualToString:RNSVGFontStyleStrings[i]]) { + return (RNSVGFontStyle)i; + } + } + return RNSVGFontStyleDEFAULT; } #pragma mark - RNSVGFontVariantLigatures -NSString* RNSVGFontVariantLigaturesToString( enum RNSVGFontVariantLigatures fw ) +NSString *RNSVGFontVariantLigaturesToString(enum RNSVGFontVariantLigatures fw) { - return RNSVGFontVariantLigaturesStrings[fw]; + return RNSVGFontVariantLigaturesStrings[fw]; } -enum RNSVGFontVariantLigatures RNSVGFontVariantLigaturesFromString( NSString* s ) +enum RNSVGFontVariantLigatures RNSVGFontVariantLigaturesFromString(NSString *s) { - if ([s length] == 0) { - return RNSVGFontVariantLigaturesDEFAULT; - } - const NSUInteger l = sizeof(RNSVGFontVariantLigaturesStrings) / sizeof(NSString*); - for (NSUInteger i = 0; i < l; i++) { - if ([s isEqualToString:RNSVGFontVariantLigaturesStrings[i]]) { - return (RNSVGFontVariantLigatures)i; - } - } + if ([s length] == 0) { return RNSVGFontVariantLigaturesDEFAULT; + } + const NSUInteger l = sizeof(RNSVGFontVariantLigaturesStrings) / sizeof(NSString *); + for (NSUInteger i = 0; i < l; i++) { + if ([s isEqualToString:RNSVGFontVariantLigaturesStrings[i]]) { + return (RNSVGFontVariantLigatures)i; + } + } + return RNSVGFontVariantLigaturesDEFAULT; } #pragma mark - RNSVGFontWeight -NSString* RNSVGFontWeightToString( enum RNSVGFontWeight fw ) +NSString *RNSVGFontWeightToString(enum RNSVGFontWeight fw) { - return RNSVGFontWeightStrings[fw]; + return RNSVGFontWeightStrings[fw]; } -NSInteger RNSVGFontWeightFromString( NSString* s ) +NSInteger RNSVGFontWeightFromString(NSString *s) { - if ([s length] == 0) { - return -1; - } - const NSInteger l = sizeof(RNSVGFontWeightStrings) / sizeof(NSString*); - for (NSInteger i = 0; i < l; i++) { - if ([s isEqualToString:RNSVGFontWeightStrings[i]]) { - return i; - } - } + if ([s length] == 0) { return -1; + } + const NSInteger l = sizeof(RNSVGFontWeightStrings) / sizeof(NSString *); + for (NSInteger i = 0; i < l; i++) { + if ([s isEqualToString:RNSVGFontWeightStrings[i]]) { + return i; + } + } + return -1; } #pragma mark - RNSVGTextAnchor -NSString* RNSVGTextAnchorToString( enum RNSVGTextAnchor fw ) +NSString *RNSVGTextAnchorToString(enum RNSVGTextAnchor fw) { - return RNSVGTextAnchorStrings[fw]; + return RNSVGTextAnchorStrings[fw]; } -enum RNSVGTextAnchor RNSVGTextAnchorFromString( NSString* s ) +enum RNSVGTextAnchor RNSVGTextAnchorFromString(NSString *s) { - if ([s length] == 0) { - return RNSVGTextAnchorDEFAULT; - } - const NSUInteger l = sizeof(RNSVGTextAnchorStrings) / sizeof(NSString*); - for (NSUInteger i = 0; i < l; i++) { - if ([s isEqualToString:RNSVGTextAnchorStrings[i]]) { - return (RNSVGTextAnchor)i; - } - } + if ([s length] == 0) { return RNSVGTextAnchorDEFAULT; + } + const NSUInteger l = sizeof(RNSVGTextAnchorStrings) / sizeof(NSString *); + for (NSUInteger i = 0; i < l; i++) { + if ([s isEqualToString:RNSVGTextAnchorStrings[i]]) { + return (RNSVGTextAnchor)i; + } + } + return RNSVGTextAnchorDEFAULT; } #pragma mark - RNSVGTextDecoration -NSString* RNSVGTextDecorationToString( enum RNSVGTextDecoration fw ) +NSString *RNSVGTextDecorationToString(enum RNSVGTextDecoration fw) { - return RNSVGTextDecorationStrings[fw]; + return RNSVGTextDecorationStrings[fw]; } -enum RNSVGTextDecoration RNSVGTextDecorationFromString( NSString* s ) +enum RNSVGTextDecoration RNSVGTextDecorationFromString(NSString *s) { - if ([s length] == 0) { - return RNSVGTextDecorationDEFAULT; - } - const NSUInteger l = sizeof(RNSVGTextDecorationStrings) / sizeof(NSString*); - for (NSUInteger i = 0; i < l; i++) { - if ([s isEqualToString:RNSVGTextDecorationStrings[i]]) { - return (RNSVGTextDecoration)i; - } - } + if ([s length] == 0) { return RNSVGTextDecorationDEFAULT; + } + const NSUInteger l = sizeof(RNSVGTextDecorationStrings) / sizeof(NSString *); + for (NSUInteger i = 0; i < l; i++) { + if ([s isEqualToString:RNSVGTextDecorationStrings[i]]) { + return (RNSVGTextDecoration)i; + } + } + return RNSVGTextDecorationDEFAULT; } #pragma mark - RNSVGTextLengthAdjust -NSString* RNSVGTextLengthAdjustToString( enum RNSVGTextLengthAdjust fw ) +NSString *RNSVGTextLengthAdjustToString(enum RNSVGTextLengthAdjust fw) { - return RNSVGTextLengthAdjustStrings[fw]; + return RNSVGTextLengthAdjustStrings[fw]; } -enum RNSVGTextLengthAdjust RNSVGTextLengthAdjustFromString( NSString* s ) +enum RNSVGTextLengthAdjust RNSVGTextLengthAdjustFromString(NSString *s) { - if ([s length] == 0) { - return RNSVGTextLengthAdjustDEFAULT; - } - const NSUInteger l = sizeof(RNSVGTextLengthAdjustStrings) / sizeof(NSString*); - for (NSUInteger i = 0; i < l; i++) { - if ([s isEqualToString:RNSVGTextLengthAdjustStrings[i]]) { - return (RNSVGTextLengthAdjust)i; - } - } + if ([s length] == 0) { return RNSVGTextLengthAdjustDEFAULT; + } + const NSUInteger l = sizeof(RNSVGTextLengthAdjustStrings) / sizeof(NSString *); + for (NSUInteger i = 0; i < l; i++) { + if ([s isEqualToString:RNSVGTextLengthAdjustStrings[i]]) { + return (RNSVGTextLengthAdjust)i; + } + } + return RNSVGTextLengthAdjustDEFAULT; } #pragma mark - RNSVGTextPathMethod -NSString* RNSVGTextPathMethodToString( enum RNSVGTextPathMethod fw ) +NSString *RNSVGTextPathMethodToString(enum RNSVGTextPathMethod fw) { - return RNSVGTextPathMethodStrings[fw]; + return RNSVGTextPathMethodStrings[fw]; } -enum RNSVGTextPathMethod RNSVGTextPathMethodFromString( NSString* s ) +enum RNSVGTextPathMethod RNSVGTextPathMethodFromString(NSString *s) { - if ([s length] == 0) { - return RNSVGTextPathMethodDEFAULT; - } - const NSUInteger l = sizeof(RNSVGTextPathMethodStrings) / sizeof(NSString*); - for (NSUInteger i = 0; i < l; i++) { - if ([s isEqualToString:RNSVGTextPathMethodStrings[i]]) { - return (RNSVGTextPathMethod)i; - } - } + if ([s length] == 0) { return RNSVGTextPathMethodDEFAULT; + } + const NSUInteger l = sizeof(RNSVGTextPathMethodStrings) / sizeof(NSString *); + for (NSUInteger i = 0; i < l; i++) { + if ([s isEqualToString:RNSVGTextPathMethodStrings[i]]) { + return (RNSVGTextPathMethod)i; + } + } + return RNSVGTextPathMethodDEFAULT; } #pragma mark - RNSVGTextPathMidLine -NSString* RNSVGTextPathMidLineToString( enum RNSVGTextPathMidLine fw ) +NSString *RNSVGTextPathMidLineToString(enum RNSVGTextPathMidLine fw) { - return RNSVGTextPathMidLineStrings[fw]; + return RNSVGTextPathMidLineStrings[fw]; } -enum RNSVGTextPathMidLine RNSVGTextPathMidLineFromString( NSString* s ) +enum RNSVGTextPathMidLine RNSVGTextPathMidLineFromString(NSString *s) { - if ([s length] == 0) { - return RNSVGTextPathMidLineDEFAULT; - } - const NSUInteger l = sizeof(RNSVGTextPathMidLineStrings) / sizeof(NSString*); - for (NSUInteger i = 0; i < l; i++) { - if ([s isEqualToString:RNSVGTextPathMidLineStrings[i]]) { - return (RNSVGTextPathMidLine)i; - } - } + if ([s length] == 0) { return RNSVGTextPathMidLineDEFAULT; + } + const NSUInteger l = sizeof(RNSVGTextPathMidLineStrings) / sizeof(NSString *); + for (NSUInteger i = 0; i < l; i++) { + if ([s isEqualToString:RNSVGTextPathMidLineStrings[i]]) { + return (RNSVGTextPathMidLine)i; + } + } + return RNSVGTextPathMidLineDEFAULT; } #pragma mark - RNSVGTextPathSide -NSString* RNSVGTextPathSideToString( enum RNSVGTextPathSide fw ) +NSString *RNSVGTextPathSideToString(enum RNSVGTextPathSide fw) { - return RNSVGTextPathSideStrings[fw]; + return RNSVGTextPathSideStrings[fw]; } -enum RNSVGTextPathSide RNSVGTextPathSideFromString( NSString* s ) +enum RNSVGTextPathSide RNSVGTextPathSideFromString(NSString *s) { - if ([s length] == 0) { - return RNSVGTextPathSideDEFAULT; - } - const NSUInteger l = sizeof(RNSVGTextPathSideStrings) / sizeof(NSString*); - for (NSUInteger i = 0; i < l; i++) { - if ([s isEqualToString:RNSVGTextPathSideStrings[i]]) { - return (RNSVGTextPathSide)i; - } - } + if ([s length] == 0) { return RNSVGTextPathSideDEFAULT; + } + const NSUInteger l = sizeof(RNSVGTextPathSideStrings) / sizeof(NSString *); + for (NSUInteger i = 0; i < l; i++) { + if ([s isEqualToString:RNSVGTextPathSideStrings[i]]) { + return (RNSVGTextPathSide)i; + } + } + return RNSVGTextPathSideDEFAULT; } #pragma mark - RNSVGTextPathSpacing -NSString* RNSVGTextPathSpacingToString( enum RNSVGTextPathSpacing fw ) +NSString *RNSVGTextPathSpacingToString(enum RNSVGTextPathSpacing fw) { - return RNSVGTextPathSpacingStrings[fw]; + return RNSVGTextPathSpacingStrings[fw]; } -enum RNSVGTextPathSpacing RNSVGTextPathSpacingFromString( NSString* s ) +enum RNSVGTextPathSpacing RNSVGTextPathSpacingFromString(NSString *s) { - if ([s length] == 0) { - return RNSVGTextPathSpacingDEFAULT; - } - const NSUInteger l = sizeof(RNSVGTextPathSpacingStrings) / sizeof(NSString*); - for (NSUInteger i = 0; i < l; i++) { - if ([s isEqualToString:RNSVGTextPathSpacingStrings[i]]) { - return (RNSVGTextPathSpacing)i; - } - } + if ([s length] == 0) { return RNSVGTextPathSpacingDEFAULT; + } + const NSUInteger l = sizeof(RNSVGTextPathSpacingStrings) / sizeof(NSString *); + for (NSUInteger i = 0; i < l; i++) { + if ([s isEqualToString:RNSVGTextPathSpacingStrings[i]]) { + return (RNSVGTextPathSpacing)i; + } + } + return RNSVGTextPathSpacingDEFAULT; } diff --git a/apple/Text/RNSVGTopAlignedLabel.ios.mm b/apple/Text/RNSVGTopAlignedLabel.ios.mm index 03c1f271..c9322175 100644 --- a/apple/Text/RNSVGTopAlignedLabel.ios.mm +++ b/apple/Text/RNSVGTopAlignedLabel.ios.mm @@ -2,16 +2,18 @@ @implementation RNSVGTopAlignedLabel -- (void)drawTextInRect:(CGRect) rect +- (void)drawTextInRect:(CGRect)rect { - NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:self.text attributes:@{NSFontAttributeName:self.font}]; - rect.size.height = [attributedText boundingRectWithSize:rect.size - options:NSStringDrawingUsesLineFragmentOrigin - context:nil].size.height; - if (self.numberOfLines != 0) { - rect.size.height = MIN(rect.size.height, self.numberOfLines * self.font.lineHeight); - } - [super drawTextInRect:rect]; + NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:self.text + attributes:@{NSFontAttributeName : self.font}]; + rect.size.height = [attributedText boundingRectWithSize:rect.size + options:NSStringDrawingUsesLineFragmentOrigin + context:nil] + .size.height; + if (self.numberOfLines != 0) { + rect.size.height = MIN(rect.size.height, self.numberOfLines * self.font.lineHeight); + } + [super drawTextInRect:rect]; } @end diff --git a/apple/Text/RNSVGTopAlignedLabel.macos.mm b/apple/Text/RNSVGTopAlignedLabel.macos.mm index e5e63755..f700179f 100644 --- a/apple/Text/RNSVGTopAlignedLabel.macos.mm +++ b/apple/Text/RNSVGTopAlignedLabel.macos.mm @@ -4,52 +4,52 @@ - (NSAttributedString *)attributedText { - return self.attributedString; + return self.attributedString; } - (NSLineBreakMode)lineBreakMode { - return self.textContainer.lineBreakMode; + return self.textContainer.lineBreakMode; } - (NSInteger)numberOfLines { - return self.textContainer.maximumNumberOfLines; + return self.textContainer.maximumNumberOfLines; } - (NSString *)text { - return self.string; + return self.string; } - (NSTextAlignment)textAlignment { - return self.alignment; + return self.alignment; } - (void)setAttributedText:(NSAttributedString *)attributedString { - [self.textStorage setAttributedString:attributedString]; + [self.textStorage setAttributedString:attributedString]; } - (void)setLineBreakMode:(NSLineBreakMode)lineBreakMode { - self.textContainer.lineBreakMode = lineBreakMode; + self.textContainer.lineBreakMode = lineBreakMode; } - (void)setNumberOfLines:(NSInteger)numberOfLines { - self.textContainer.maximumNumberOfLines = numberOfLines; + self.textContainer.maximumNumberOfLines = numberOfLines; } - (void)setText:(NSString *)text { - self.string = text; + self.string = text; } - (void)setTextAlignment:(NSTextAlignment)textAlignment { - self.alignment = textAlignment; + self.alignment = textAlignment; } @end diff --git a/apple/Utils/RCTConvert+RNSVG.h b/apple/Utils/RCTConvert+RNSVG.h index bce5f06b..cc0f3711 100644 --- a/apple/Utils/RCTConvert+RNSVG.h +++ b/apple/Utils/RCTConvert+RNSVG.h @@ -6,22 +6,22 @@ * LICENSE file in the root directory of this source tree. */ -#import #import -#import "RCTConvert+RNSVG.h" +#import #import +#import "RCTConvert+RNSVG.h" #import "RNSVGCGFCRule.h" -#import "RNSVGVBMOS.h" -#import "RNSVGUnits.h" #import "RNSVGLength.h" #import "RNSVGPathParser.h" +#import "RNSVGUnits.h" +#import "RNSVGVBMOS.h" @class RNSVGBrush; @interface RCTConvert (RNSVG) -+ (RNSVGLength*)RNSVGLength:(id)json; -+ (NSArray*)RNSVGLengthArray:(id)json; ++ (RNSVGLength *)RNSVGLength:(id)json; ++ (NSArray *)RNSVGLengthArray:(id)json; + (RNSVGCGFCRule)RNSVGCGFCRule:(id)json; + (RNSVGVBMOS)RNSVGVBMOS:(id)json; + (RNSVGUnits)RNSVGUnits:(id)json; diff --git a/apple/Utils/RCTConvert+RNSVG.mm b/apple/Utils/RCTConvert+RNSVG.mm index 7cbbe5a3..148b41ad 100644 --- a/apple/Utils/RCTConvert+RNSVG.mm +++ b/apple/Utils/RCTConvert+RNSVG.mm @@ -8,184 +8,190 @@ #import "RCTConvert+RNSVG.h" +#import +#import +#import "RNSVGContextBrush.h" #import "RNSVGPainterBrush.h" #import "RNSVGSolidColorBrush.h" -#import "RNSVGContextBrush.h" -#import -#import NSRegularExpression *RNSVGDigitRegEx; @implementation RCTConvert (RNSVG) -RCT_ENUM_CONVERTER(RNSVGCGFCRule, (@{ - @"evenodd": @(kRNSVGCGFCRuleEvenodd), - @"nonzero": @(kRNSVGCGFCRuleNonzero), - }), kRNSVGCGFCRuleNonzero, intValue) +RCT_ENUM_CONVERTER( + RNSVGCGFCRule, + (@{ + @"evenodd" : @(kRNSVGCGFCRuleEvenodd), + @"nonzero" : @(kRNSVGCGFCRuleNonzero), + }), + kRNSVGCGFCRuleNonzero, + intValue) -RCT_ENUM_CONVERTER(RNSVGVBMOS, (@{ - @"meet": @(kRNSVGVBMOSMeet), - @"slice": @(kRNSVGVBMOSSlice), - @"none": @(kRNSVGVBMOSNone) - }), kRNSVGVBMOSMeet, intValue) +RCT_ENUM_CONVERTER( + RNSVGVBMOS, + (@{@"meet" : @(kRNSVGVBMOSMeet), @"slice" : @(kRNSVGVBMOSSlice), @"none" : @(kRNSVGVBMOSNone)}), + kRNSVGVBMOSMeet, + intValue) -RCT_ENUM_CONVERTER(RNSVGUnits, (@{ - @"objectBoundingBox": @(kRNSVGUnitsObjectBoundingBox), - @"userSpaceOnUse": @(kRNSVGUnitsUserSpaceOnUse), - }), kRNSVGUnitsObjectBoundingBox, intValue) +RCT_ENUM_CONVERTER( + RNSVGUnits, + (@{ + @"objectBoundingBox" : @(kRNSVGUnitsObjectBoundingBox), + @"userSpaceOnUse" : @(kRNSVGUnitsUserSpaceOnUse), + }), + kRNSVGUnitsObjectBoundingBox, + intValue) + (RNSVGBrush *)RNSVGBrush:(id)json { - if ([json isKindOfClass:[NSNumber class]]) { - return [[RNSVGSolidColorBrush alloc] initWithNumber:json]; + if ([json isKindOfClass:[NSNumber class]]) { + return [[RNSVGSolidColorBrush alloc] initWithNumber:json]; + } + if ([json isKindOfClass:[NSString class]]) { + NSString *value = [self NSString:json]; + if (!RNSVGDigitRegEx) { + RNSVGDigitRegEx = [NSRegularExpression regularExpressionWithPattern:@"[0-9.-]+" + options:NSRegularExpressionCaseInsensitive + error:nil]; } - if ([json isKindOfClass:[NSString class]]) { - NSString *value = [self NSString:json]; - if (!RNSVGDigitRegEx) { - RNSVGDigitRegEx = [NSRegularExpression regularExpressionWithPattern:@"[0-9.-]+" options:NSRegularExpressionCaseInsensitive error:nil]; - } - NSArray *_matches = [RNSVGDigitRegEx matchesInString:value options:0 range:NSMakeRange(0, [value length])]; - NSMutableArray *output = [NSMutableArray array]; - NSUInteger i = 0; - [output addObject:[NSNumber numberWithInteger:0]]; - for (NSTextCheckingResult *match in _matches) { - NSString* strNumber = [value substringWithRange:match.range]; - [output addObject:[NSNumber numberWithDouble:(i++ < 3 ? strNumber.doubleValue / 255 : strNumber.doubleValue)]]; - } - if ([output count] < 5) { - [output addObject:[NSNumber numberWithDouble:1]]; - } - return [[RNSVGSolidColorBrush alloc] initWithArray:output]; + NSArray *_matches = [RNSVGDigitRegEx matchesInString:value + options:0 + range:NSMakeRange(0, [value length])]; + NSMutableArray *output = [NSMutableArray array]; + NSUInteger i = 0; + [output addObject:[NSNumber numberWithInteger:0]]; + for (NSTextCheckingResult *match in _matches) { + NSString *strNumber = [value substringWithRange:match.range]; + [output addObject:[NSNumber numberWithDouble:(i++ < 3 ? strNumber.doubleValue / 255 : strNumber.doubleValue)]]; } - NSDictionary *dict = [self NSDictionary:json]; - int type = [dict[@"type"] intValue]; + if ([output count] < 5) { + [output addObject:[NSNumber numberWithDouble:1]]; + } + return [[RNSVGSolidColorBrush alloc] initWithArray:output]; + } + NSDictionary *dict = [self NSDictionary:json]; + int type = [dict[@"type"] intValue]; - switch (type) { - case 0: // solid color + switch (type) { + case 0: // solid color // These are probably expensive allocations since it's often the same value. // We should memoize colors but look ups may be just as expensive. - { - NSArray *arr = @[@(0), dict[@"value"]]; - return [[RNSVGSolidColorBrush alloc] initWithArray:arr]; - } - case 1: // brush - { - NSArray *arr = @[@(1), dict[@"brushRef"]]; - return [[RNSVGPainterBrush alloc] initWithArray:arr]; - } - case 2: // currentColor - return [[RNSVGBrush alloc] initWithArray:nil]; - case 3: // context-fill - return [[RNSVGContextBrush alloc] initFill]; - case 4: // context-stroke - return [[RNSVGContextBrush alloc] initStroke]; - default: - RCTLogError(@"Unknown brush type: %zd", (unsigned long)type); - return nil; + { + NSArray *arr = @[ @(0), dict[@"value"] ]; + return [[RNSVGSolidColorBrush alloc] initWithArray:arr]; } - + case 1: // brush + { + NSArray *arr = @[ @(1), dict[@"brushRef"] ]; + return [[RNSVGPainterBrush alloc] initWithArray:arr]; + } + case 2: // currentColor + return [[RNSVGBrush alloc] initWithArray:nil]; + case 3: // context-fill + return [[RNSVGContextBrush alloc] initFill]; + case 4: // context-stroke + return [[RNSVGContextBrush alloc] initStroke]; + default: + RCTLogError(@"Unknown brush type: %zd", (unsigned long)type); + return nil; + } } + (RNSVGPathParser *)RNSVGCGPath:(NSString *)d { - return [[RNSVGPathParser alloc] initWithPathString: d]; + return [[RNSVGPathParser alloc] initWithPathString:d]; } + (RNSVGLength *)RNSVGLength:(id)json { - if ([json isKindOfClass:[NSNumber class]]) { - return [RNSVGLength lengthWithNumber:(CGFloat)[json doubleValue]]; - } else if ([json isKindOfClass:[NSString class]]) { - NSString *stringValue = (NSString *)json; - return [RNSVGLength lengthWithString:stringValue]; - } else { - return [[RNSVGLength alloc] init]; - } + if ([json isKindOfClass:[NSNumber class]]) { + return [RNSVGLength lengthWithNumber:(CGFloat)[json doubleValue]]; + } else if ([json isKindOfClass:[NSString class]]) { + NSString *stringValue = (NSString *)json; + return [RNSVGLength lengthWithString:stringValue]; + } else { + return [[RNSVGLength alloc] init]; + } } -+ (NSArray*)RNSVGLengthArray:(id)json ++ (NSArray *)RNSVGLengthArray:(id)json { - if ([json isKindOfClass:[NSNumber class]]) { - RNSVGLength* length = [RNSVGLength lengthWithNumber:(CGFloat)[json doubleValue]]; - return [NSArray arrayWithObject:length]; - } else if ([json isKindOfClass:[NSArray class]]) { - NSArray *arrayValue = (NSArray*)json; - NSMutableArray* lengths = [NSMutableArray arrayWithCapacity:[arrayValue count]]; - for (id obj in arrayValue) { - [lengths addObject:[self RNSVGLength:obj]]; - } - return lengths; - } else if ([json isKindOfClass:[NSString class]]) { - NSString *stringValue = (NSString *)json; - RNSVGLength* length = [RNSVGLength lengthWithString:stringValue]; - return [NSArray arrayWithObject:length]; - } else { - return nil; + if ([json isKindOfClass:[NSNumber class]]) { + RNSVGLength *length = [RNSVGLength lengthWithNumber:(CGFloat)[json doubleValue]]; + return [NSArray arrayWithObject:length]; + } else if ([json isKindOfClass:[NSArray class]]) { + NSArray *arrayValue = (NSArray *)json; + NSMutableArray *lengths = [NSMutableArray arrayWithCapacity:[arrayValue count]]; + for (id obj in arrayValue) { + [lengths addObject:[self RNSVGLength:obj]]; } + return lengths; + } else if ([json isKindOfClass:[NSString class]]) { + NSString *stringValue = (NSString *)json; + RNSVGLength *length = [RNSVGLength lengthWithString:stringValue]; + return [NSArray arrayWithObject:length]; + } else { + return nil; + } } + (CGRect)RNSVGCGRect:(id)json offset:(NSUInteger)offset { - NSArray *arr = [self NSArray:json]; - if (arr.count < offset + 4) { - RCTLogError(@"Too few elements in array (expected at least %zd): %@", (ssize_t)(4 + offset), arr); - return CGRectZero; - } - return (CGRect){ - {[self CGFloat:arr[offset]], [self CGFloat:arr[offset + 1]]}, - {[self CGFloat:arr[offset + 2]], [self CGFloat:arr[offset + 3]]}, - }; + NSArray *arr = [self NSArray:json]; + if (arr.count < offset + 4) { + RCTLogError(@"Too few elements in array (expected at least %zd): %@", (ssize_t)(4 + offset), arr); + return CGRectZero; + } + return (CGRect){ + {[self CGFloat:arr[offset]], [self CGFloat:arr[offset + 1]]}, + {[self CGFloat:arr[offset + 2]], [self CGFloat:arr[offset + 3]]}, + }; } + (RNSVGColor *)RNSVGColor:(id)json offset:(NSUInteger)offset { - NSArray *arr = [self NSArray:json]; - if (arr.count == offset + 1) { - return [self RNSVGColor:[arr objectAtIndex:offset]]; - } - if (arr.count < offset + 4) { - RCTLogError(@"Too few elements in array (expected at least %zd): %@", (ssize_t)(4 + offset), arr); - return nil; - } - return [self RNSVGColor:[arr subarrayWithRange:(NSRange){offset, 4}]]; + NSArray *arr = [self NSArray:json]; + if (arr.count == offset + 1) { + return [self RNSVGColor:[arr objectAtIndex:offset]]; + } + if (arr.count < offset + 4) { + RCTLogError(@"Too few elements in array (expected at least %zd): %@", (ssize_t)(4 + offset), arr); + return nil; + } + return [self RNSVGColor:[arr subarrayWithRange:(NSRange){offset, 4}]]; } + (CGGradientRef)RNSVGCGGradient:(id)json { - NSArray *arr = [self NSArray:json]; - NSUInteger count = arr.count / 2; - NSUInteger values = count * 5; - NSUInteger offsetIndex = values - count; - CGFloat colorsAndOffsets[values]; - for (NSUInteger i = 0; i < count; i++) { - NSUInteger stopIndex = i * 2; - CGFloat offset = (CGFloat)[arr[stopIndex] doubleValue]; - NSUInteger argb = [self NSUInteger:arr[stopIndex + 1]]; + NSArray *arr = [self NSArray:json]; + NSUInteger count = arr.count / 2; + NSUInteger values = count * 5; + NSUInteger offsetIndex = values - count; + CGFloat colorsAndOffsets[values]; + for (NSUInteger i = 0; i < count; i++) { + NSUInteger stopIndex = i * 2; + CGFloat offset = (CGFloat)[arr[stopIndex] doubleValue]; + NSUInteger argb = [self NSUInteger:arr[stopIndex + 1]]; - CGFloat a = ((argb >> 24) & 0xFF) / 255.0; - CGFloat r = ((argb >> 16) & 0xFF) / 255.0; - CGFloat g = ((argb >> 8) & 0xFF) / 255.0; - CGFloat b = (argb & 0xFF) / 255.0; + CGFloat a = ((argb >> 24) & 0xFF) / 255.0; + CGFloat r = ((argb >> 16) & 0xFF) / 255.0; + CGFloat g = ((argb >> 8) & 0xFF) / 255.0; + CGFloat b = (argb & 0xFF) / 255.0; - NSUInteger colorIndex = i * 4; - colorsAndOffsets[colorIndex] = r; - colorsAndOffsets[colorIndex + 1] = g; - colorsAndOffsets[colorIndex + 2] = b; - colorsAndOffsets[colorIndex + 3] = a; + NSUInteger colorIndex = i * 4; + colorsAndOffsets[colorIndex] = r; + colorsAndOffsets[colorIndex + 1] = g; + colorsAndOffsets[colorIndex + 2] = b; + colorsAndOffsets[colorIndex + 3] = a; - colorsAndOffsets[offsetIndex + i] = fmax(0, fmin(offset, 1)); - } + colorsAndOffsets[offsetIndex + i] = fmax(0, fmin(offset, 1)); + } - CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB(); - CGGradientRef gradient = CGGradientCreateWithColorComponents( - rgb, - colorsAndOffsets, - colorsAndOffsets + offsetIndex, - count - ); - CGColorSpaceRelease(rgb); - return (CGGradientRef)CFAutorelease(gradient); + CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB(); + CGGradientRef gradient = + CGGradientCreateWithColorComponents(rgb, colorsAndOffsets, colorsAndOffsets + offsetIndex, count); + CGColorSpaceRelease(rgb); + return (CGGradientRef)CFAutorelease(gradient); } @end - diff --git a/apple/Utils/RNSVGBezierElement.h b/apple/Utils/RNSVGBezierElement.h index bad9f7c6..29ca0eee 100644 --- a/apple/Utils/RNSVGBezierElement.h +++ b/apple/Utils/RNSVGBezierElement.h @@ -19,8 +19,7 @@ @property (nonatomic, assign) CGPoint controlPoint2; // Instance creation -+ (instancetype) elementWithPathElement: (CGPathElement) element; -+ (NSArray *) elementsFromCGPath:(CGPathRef)path; ++ (instancetype)elementWithPathElement:(CGPathElement)element; ++ (NSArray *)elementsFromCGPath:(CGPathRef)path; @end - diff --git a/apple/Utils/RNSVGBezierElement.mm b/apple/Utils/RNSVGBezierElement.mm index b79c3bec..b682a848 100644 --- a/apple/Utils/RNSVGBezierElement.mm +++ b/apple/Utils/RNSVGBezierElement.mm @@ -9,69 +9,63 @@ #pragma mark - Bezier Element - @implementation RNSVGBezierElement -- (instancetype) init +- (instancetype)init { - self = [super init]; - if (self) - { - _elementType = kCGPathElementMoveToPoint; - _point = RNSVGNULLPOINT; - _controlPoint1 = RNSVGNULLPOINT; - _controlPoint2 = RNSVGNULLPOINT; - } - return self; + self = [super init]; + if (self) { + _elementType = kCGPathElementMoveToPoint; + _point = RNSVGNULLPOINT; + _controlPoint1 = RNSVGNULLPOINT; + _controlPoint2 = RNSVGNULLPOINT; + } + return self; } -+ (instancetype) elementWithPathElement: (CGPathElement) element ++ (instancetype)elementWithPathElement:(CGPathElement)element { - RNSVGBezierElement *newElement = [[self alloc] init]; - newElement.elementType = element.type; + RNSVGBezierElement *newElement = [[self alloc] init]; + newElement.elementType = element.type; - switch (newElement.elementType) - { - case kCGPathElementCloseSubpath: - break; - case kCGPathElementMoveToPoint: - case kCGPathElementAddLineToPoint: - { - newElement.point = element.points[0]; - break; - } - case kCGPathElementAddQuadCurveToPoint: - { - newElement.point = element.points[1]; - newElement.controlPoint1 = element.points[0]; - break; - } - case kCGPathElementAddCurveToPoint: - { - newElement.point = element.points[2]; - newElement.controlPoint1 = element.points[0]; - newElement.controlPoint2 = element.points[1]; - break; - } - default: - break; + switch (newElement.elementType) { + case kCGPathElementCloseSubpath: + break; + case kCGPathElementMoveToPoint: + case kCGPathElementAddLineToPoint: { + newElement.point = element.points[0]; + break; } + case kCGPathElementAddQuadCurveToPoint: { + newElement.point = element.points[1]; + newElement.controlPoint1 = element.points[0]; + break; + } + case kCGPathElementAddCurveToPoint: { + newElement.point = element.points[2]; + newElement.controlPoint1 = element.points[0]; + newElement.controlPoint2 = element.points[1]; + break; + } + default: + break; + } - return newElement; + return newElement; } // Convert one element to RNSVGBezierElement and save to array void RNSVGBezierElement_getBezierElements(void *info, const CGPathElement *element) { - NSMutableArray *bezierElements = (__bridge NSMutableArray *)info; - if (element) - [bezierElements addObject:[RNSVGBezierElement elementWithPathElement:*element]]; + NSMutableArray *bezierElements = (__bridge NSMutableArray *)info; + if (element) + [bezierElements addObject:[RNSVGBezierElement elementWithPathElement:*element]]; } // Retrieve array of component elements -+ (NSArray *) elementsFromCGPath:(CGPathRef)path ++ (NSArray *)elementsFromCGPath:(CGPathRef)path { - NSMutableArray *elements = [NSMutableArray array]; - CGPathApply(path, (__bridge void *)elements, RNSVGBezierElement_getBezierElements); - return elements; + NSMutableArray *elements = [NSMutableArray array]; + CGPathApply(path, (__bridge void *)elements, RNSVGBezierElement_getBezierElements); + return elements; } @end - diff --git a/apple/Utils/RNSVGCGFCRule.h b/apple/Utils/RNSVGCGFCRule.h index 160b0ef3..b49dd7b0 100644 --- a/apple/Utils/RNSVGCGFCRule.h +++ b/apple/Utils/RNSVGCGFCRule.h @@ -7,6 +7,6 @@ */ typedef CF_ENUM(int32_t, RNSVGCGFCRule) { - kRNSVGCGFCRuleEvenodd, - kRNSVGCGFCRuleNonzero + kRNSVGCGFCRuleEvenodd, + kRNSVGCGFCRuleNonzero }; \ No newline at end of file diff --git a/apple/Utils/RNSVGFabricConversions.h b/apple/Utils/RNSVGFabricConversions.h index 822f2f98..966cde98 100644 --- a/apple/Utils/RNSVGFabricConversions.h +++ b/apple/Utils/RNSVGFabricConversions.h @@ -1,178 +1,192 @@ -#import "RNSVGPainterBrush.h" -#import "RNSVGSolidColorBrush.h" #import "RNSVGContextBrush.h" -#import "RNSVGRenderable.h" -#import "RNSVGLength.h" -#import "RNSVGVBMOS.h" #import "RNSVGGroup.h" +#import "RNSVGLength.h" +#import "RNSVGPainterBrush.h" +#import "RNSVGRenderable.h" +#import "RNSVGSolidColorBrush.h" +#import "RNSVGVBMOS.h" -#import "RCTFabricComponentsPlugins.h" #import "RCTConversions.h" +#import "RCTFabricComponentsPlugins.h" -template +template RNSVGBrush *brushFromColorStruct(T fillObject) { - int type = fillObject.type; + int type = fillObject.type; - switch (type) { - case -1: // empty struct - return nil; - case 0: // solid color - { - // These are probably expensive allocations since it's often the same value. - // We should memoize colors but look ups may be just as expensive. - RNSVGColor *color = RCTUIColorFromSharedColor(fillObject.value) ?: [RNSVGColor clearColor]; - return [[RNSVGSolidColorBrush alloc] initWithColor:color]; - } - case 1: // brush - { - NSArray *arr = @[@(type), RCTNSStringFromString(fillObject.brushRef)]; - return [[RNSVGPainterBrush alloc] initWithArray:arr]; - } - case 2: // currentColor - return [[RNSVGBrush alloc] initWithArray:nil]; - case 3: // context-fill - return [[RNSVGContextBrush alloc] initFill]; - case 4: // context-stroke - return [[RNSVGContextBrush alloc] initStroke]; - default: - RCTLogError(@"Unknown brush type: %d", type); - return nil; + switch (type) { + case -1: // empty struct + return nil; + case 0: // solid color + { + // These are probably expensive allocations since it's often the same value. + // We should memoize colors but look ups may be just as expensive. + RNSVGColor *color = RCTUIColorFromSharedColor(fillObject.value) ?: [RNSVGColor clearColor]; + return [[RNSVGSolidColorBrush alloc] initWithColor:color]; } + case 1: // brush + { + NSArray *arr = @[ @(type), RCTNSStringFromString(fillObject.brushRef) ]; + return [[RNSVGPainterBrush alloc] initWithArray:arr]; + } + case 2: // currentColor + return [[RNSVGBrush alloc] initWithArray:nil]; + case 3: // context-fill + return [[RNSVGContextBrush alloc] initFill]; + case 4: // context-stroke + return [[RNSVGContextBrush alloc] initStroke]; + default: + RCTLogError(@"Unknown brush type: %d", type); + return nil; + } } -template +template void setCommonNodeProps(T nodeProps, RNSVGNode *node) { - node.name = RCTNSStringFromStringNilIfEmpty(nodeProps.name); - node.opacity = nodeProps.opacity; - if (nodeProps.matrix.size() == 6) { - node.matrix = CGAffineTransformMake(nodeProps.matrix.at(0), nodeProps.matrix.at(1), nodeProps.matrix.at(2), nodeProps.matrix.at(3), nodeProps.matrix.at(4), nodeProps.matrix.at(5)); - } - CATransform3D transform3d = RCTCATransform3DFromTransformMatrix(nodeProps.transform); - CGAffineTransform transform = CATransform3DGetAffineTransform(transform3d); - node.invTransform = CGAffineTransformInvert(transform); - node.transforms = transform; - node.mask = RCTNSStringFromStringNilIfEmpty(nodeProps.mask); - node.markerStart = RCTNSStringFromStringNilIfEmpty(nodeProps.markerStart); - node.markerMid = RCTNSStringFromStringNilIfEmpty(nodeProps.markerMid); - node.markerEnd = RCTNSStringFromStringNilIfEmpty(nodeProps.markerEnd); - node.clipPath = RCTNSStringFromStringNilIfEmpty(nodeProps.clipPath); - node.clipRule = nodeProps.clipRule == 0 ? kRNSVGCGFCRuleEvenodd : kRNSVGCGFCRuleNonzero; - node.responsible = nodeProps.responsible; - // onLayout - node.display = RCTNSStringFromStringNilIfEmpty(nodeProps.display); - switch (nodeProps.pointerEvents) { - case facebook::react::PointerEventsMode::Auto: - node.pointerEvents = RCTPointerEventsUnspecified; - case facebook::react::PointerEventsMode::None: - node.pointerEvents = RCTPointerEventsNone; - case facebook::react::PointerEventsMode::BoxNone: - node.pointerEvents = RCTPointerEventsBoxNone; - case facebook::react::PointerEventsMode::BoxOnly: - node.pointerEvents = RCTPointerEventsBoxOnly; - default: - node.pointerEvents = RCTPointerEventsUnspecified; - } + node.name = RCTNSStringFromStringNilIfEmpty(nodeProps.name); + node.opacity = nodeProps.opacity; + if (nodeProps.matrix.size() == 6) { + node.matrix = CGAffineTransformMake( + nodeProps.matrix.at(0), + nodeProps.matrix.at(1), + nodeProps.matrix.at(2), + nodeProps.matrix.at(3), + nodeProps.matrix.at(4), + nodeProps.matrix.at(5)); + } + CATransform3D transform3d = RCTCATransform3DFromTransformMatrix(nodeProps.transform); + CGAffineTransform transform = CATransform3DGetAffineTransform(transform3d); + node.invTransform = CGAffineTransformInvert(transform); + node.transforms = transform; + node.mask = RCTNSStringFromStringNilIfEmpty(nodeProps.mask); + node.markerStart = RCTNSStringFromStringNilIfEmpty(nodeProps.markerStart); + node.markerMid = RCTNSStringFromStringNilIfEmpty(nodeProps.markerMid); + node.markerEnd = RCTNSStringFromStringNilIfEmpty(nodeProps.markerEnd); + node.clipPath = RCTNSStringFromStringNilIfEmpty(nodeProps.clipPath); + node.clipRule = nodeProps.clipRule == 0 ? kRNSVGCGFCRuleEvenodd : kRNSVGCGFCRuleNonzero; + node.responsible = nodeProps.responsible; + // onLayout + node.display = RCTNSStringFromStringNilIfEmpty(nodeProps.display); + switch (nodeProps.pointerEvents) { + case facebook::react::PointerEventsMode::Auto: + node.pointerEvents = RCTPointerEventsUnspecified; + case facebook::react::PointerEventsMode::None: + node.pointerEvents = RCTPointerEventsNone; + case facebook::react::PointerEventsMode::BoxNone: + node.pointerEvents = RCTPointerEventsBoxNone; + case facebook::react::PointerEventsMode::BoxOnly: + node.pointerEvents = RCTPointerEventsBoxOnly; + default: + node.pointerEvents = RCTPointerEventsUnspecified; + } } -static NSMutableArray *createLengthArrayFromStrings(std::vector stringArray) { - if (stringArray.empty()) { - return nil; - } - NSMutableArray *lengthArray = [NSMutableArray new]; - for (auto str : stringArray) { - RNSVGLength *lengthFromString = [RNSVGLength lengthWithString:RCTNSStringFromString(str)]; - [lengthArray addObject:lengthFromString]; - } - return lengthArray; +static NSMutableArray *createLengthArrayFromStrings(std::vector stringArray) +{ + if (stringArray.empty()) { + return nil; + } + NSMutableArray *lengthArray = [NSMutableArray new]; + for (auto str : stringArray) { + RNSVGLength *lengthFromString = [RNSVGLength lengthWithString:RCTNSStringFromString(str)]; + [lengthArray addObject:lengthFromString]; + } + return lengthArray; } -template +template void setCommonRenderableProps(T renderableProps, RNSVGRenderable *renderableNode) { - setCommonNodeProps(renderableProps, renderableNode); - renderableNode.fill = brushFromColorStruct(renderableProps.fill); - renderableNode.fillOpacity = renderableProps.fillOpacity; - renderableNode.fillRule = renderableProps.fillRule == 0 ? kRNSVGCGFCRuleEvenodd : kRNSVGCGFCRuleNonzero; - renderableNode.stroke = brushFromColorStruct(renderableProps.stroke); - renderableNode.strokeOpacity = renderableProps.strokeOpacity; - renderableNode.strokeWidth = [RNSVGLength lengthWithString:RCTNSStringFromString(renderableProps.strokeWidth)]; - renderableNode.strokeLinecap = renderableProps.strokeLinecap == 0 ? kCGLineCapButt : renderableProps.strokeLinecap == 1 ? kCGLineCapRound : kCGLineCapSquare; - renderableNode.strokeLinejoin = renderableProps.strokeLinejoin == 0 ? kCGLineJoinMiter : renderableProps.strokeLinejoin == 1 ? kCGLineJoinRound : kCGLineJoinBevel; - renderableNode.strokeDasharray = createLengthArrayFromStrings(renderableProps.strokeDasharray); - renderableNode.strokeDashoffset = renderableProps.strokeDashoffset; - renderableNode.strokeMiterlimit = renderableProps.strokeMiterlimit; - renderableNode.vectorEffect = renderableProps.vectorEffect == 0 ? kRNSVGVectorEffectDefault : renderableProps.vectorEffect == 1 ? kRNSVGVectorEffectNonScalingStroke : renderableProps.vectorEffect == 2 ? kRNSVGVectorEffectInherit : kRNSVGVectorEffectUri; - if (renderableProps.propList.size() > 0) { - NSMutableArray *propArray = [NSMutableArray new]; - for (auto str : renderableProps.propList) { - [propArray addObject:RCTNSStringFromString(str)]; - } - renderableNode.propList = propArray; + setCommonNodeProps(renderableProps, renderableNode); + renderableNode.fill = brushFromColorStruct(renderableProps.fill); + renderableNode.fillOpacity = renderableProps.fillOpacity; + renderableNode.fillRule = renderableProps.fillRule == 0 ? kRNSVGCGFCRuleEvenodd : kRNSVGCGFCRuleNonzero; + renderableNode.stroke = brushFromColorStruct(renderableProps.stroke); + renderableNode.strokeOpacity = renderableProps.strokeOpacity; + renderableNode.strokeWidth = [RNSVGLength lengthWithString:RCTNSStringFromString(renderableProps.strokeWidth)]; + renderableNode.strokeLinecap = renderableProps.strokeLinecap == 0 ? kCGLineCapButt + : renderableProps.strokeLinecap == 1 ? kCGLineCapRound + : kCGLineCapSquare; + renderableNode.strokeLinejoin = renderableProps.strokeLinejoin == 0 ? kCGLineJoinMiter + : renderableProps.strokeLinejoin == 1 ? kCGLineJoinRound + : kCGLineJoinBevel; + renderableNode.strokeDasharray = createLengthArrayFromStrings(renderableProps.strokeDasharray); + renderableNode.strokeDashoffset = renderableProps.strokeDashoffset; + renderableNode.strokeMiterlimit = renderableProps.strokeMiterlimit; + renderableNode.vectorEffect = renderableProps.vectorEffect == 0 ? kRNSVGVectorEffectDefault + : renderableProps.vectorEffect == 1 ? kRNSVGVectorEffectNonScalingStroke + : renderableProps.vectorEffect == 2 ? kRNSVGVectorEffectInherit + : kRNSVGVectorEffectUri; + if (renderableProps.propList.size() > 0) { + NSMutableArray *propArray = [NSMutableArray new]; + for (auto str : renderableProps.propList) { + [propArray addObject:RCTNSStringFromString(str)]; } + renderableNode.propList = propArray; + } } static void addValueToDict(NSMutableDictionary *dict, std::string value, NSString *key) { - NSString *valueOrNil = RCTNSStringFromStringNilIfEmpty(value); - if (valueOrNil) { - dict[key] = valueOrNil; - } + NSString *valueOrNil = RCTNSStringFromStringNilIfEmpty(value); + if (valueOrNil) { + dict[key] = valueOrNil; + } } -template +template NSDictionary *parseFontStruct(T fontStruct) { - NSMutableDictionary *fontDict = [NSMutableDictionary new]; - - // TODO: do it better maybe - addValueToDict(fontDict, fontStruct.fontStyle, @"fontStyle"); - addValueToDict(fontDict, fontStruct.fontVariant, @"fontVariant"); - addValueToDict(fontDict, fontStruct.fontWeight, @"fontWeight"); - addValueToDict(fontDict, fontStruct.fontStretch, @"fontStretch"); - addValueToDict(fontDict, fontStruct.fontSize, @"fontSize"); - addValueToDict(fontDict, fontStruct.fontFamily, @"fontFamily"); - addValueToDict(fontDict, fontStruct.textAnchor, @"textAnchor"); - addValueToDict(fontDict, fontStruct.textDecoration, @"textDecoration"); - addValueToDict(fontDict, fontStruct.letterSpacing, @"letterSpacing"); - addValueToDict(fontDict, fontStruct.wordSpacing, @"wordSpacing"); - addValueToDict(fontDict, fontStruct.kerning, @"kerning"); - addValueToDict(fontDict, fontStruct.fontFeatureSettings, @"fontFeatureSettings"); - addValueToDict(fontDict, fontStruct.fontVariantLigatures, @"fontVariantLigatures"); - addValueToDict(fontDict, fontStruct.fontVariationSettings, @"fontVariationSettings"); - return [NSDictionary dictionaryWithDictionary:fontDict]; + NSMutableDictionary *fontDict = [NSMutableDictionary new]; + + // TODO: do it better maybe + addValueToDict(fontDict, fontStruct.fontStyle, @"fontStyle"); + addValueToDict(fontDict, fontStruct.fontVariant, @"fontVariant"); + addValueToDict(fontDict, fontStruct.fontWeight, @"fontWeight"); + addValueToDict(fontDict, fontStruct.fontStretch, @"fontStretch"); + addValueToDict(fontDict, fontStruct.fontSize, @"fontSize"); + addValueToDict(fontDict, fontStruct.fontFamily, @"fontFamily"); + addValueToDict(fontDict, fontStruct.textAnchor, @"textAnchor"); + addValueToDict(fontDict, fontStruct.textDecoration, @"textDecoration"); + addValueToDict(fontDict, fontStruct.letterSpacing, @"letterSpacing"); + addValueToDict(fontDict, fontStruct.wordSpacing, @"wordSpacing"); + addValueToDict(fontDict, fontStruct.kerning, @"kerning"); + addValueToDict(fontDict, fontStruct.fontFeatureSettings, @"fontFeatureSettings"); + addValueToDict(fontDict, fontStruct.fontVariantLigatures, @"fontVariantLigatures"); + addValueToDict(fontDict, fontStruct.fontVariationSettings, @"fontVariationSettings"); + return [NSDictionary dictionaryWithDictionary:fontDict]; } -template +template void setCommonGroupProps(T groupProps, RNSVGGroup *groupNode) { - setCommonRenderableProps(groupProps, groupNode); - - if (RCTNSStringFromStringNilIfEmpty(groupProps.fontSize)) { - groupNode.font = @{ @"fontSize": RCTNSStringFromString(groupProps.fontSize) }; - } - if (RCTNSStringFromStringNilIfEmpty(groupProps.fontWeight)) { - groupNode.font = @{ @"fontWeight": RCTNSStringFromString(groupProps.fontWeight) }; - } - NSDictionary *fontDict = parseFontStruct(groupProps.font); - if (groupNode.font == nil || fontDict.count > 0) { - // some of text's rendering logic requires that `font` is not nil so we always set it - // even if to an empty dict - groupNode.font = fontDict; - } + setCommonRenderableProps(groupProps, groupNode); + + if (RCTNSStringFromStringNilIfEmpty(groupProps.fontSize)) { + groupNode.font = @{@"fontSize" : RCTNSStringFromString(groupProps.fontSize)}; + } + if (RCTNSStringFromStringNilIfEmpty(groupProps.fontWeight)) { + groupNode.font = @{@"fontWeight" : RCTNSStringFromString(groupProps.fontWeight)}; + } + NSDictionary *fontDict = parseFontStruct(groupProps.font); + if (groupNode.font == nil || fontDict.count > 0) { + // some of text's rendering logic requires that `font` is not nil so we always set it + // even if to an empty dict + groupNode.font = fontDict; + } } static RNSVGVBMOS intToRNSVGVBMOS(int value) { - switch (value) { - case 0: - return kRNSVGVBMOSMeet; - case 1: - return kRNSVGVBMOSSlice; - case 2: - return kRNSVGVBMOSNone; - default: - return kRNSVGVBMOSMeet; - } + switch (value) { + case 0: + return kRNSVGVBMOSMeet; + case 1: + return kRNSVGVBMOSSlice; + case 2: + return kRNSVGVBMOSNone; + default: + return kRNSVGVBMOSMeet; + } } diff --git a/apple/Utils/RNSVGLength.h b/apple/Utils/RNSVGLength.h index 58f74490..af24ef98 100644 --- a/apple/Utils/RNSVGLength.h +++ b/apple/Utils/RNSVGLength.h @@ -5,17 +5,17 @@ // https://www.w3.org/TR/SVG/types.html#InterfaceSVGLength typedef CF_ENUM(unsigned short, RNSVGLengthUnitType) { - SVG_LENGTHTYPE_UNKNOWN, - SVG_LENGTHTYPE_NUMBER, - SVG_LENGTHTYPE_PERCENTAGE, - SVG_LENGTHTYPE_EMS, - SVG_LENGTHTYPE_EXS, - SVG_LENGTHTYPE_PX, - SVG_LENGTHTYPE_CM, - SVG_LENGTHTYPE_MM, - SVG_LENGTHTYPE_IN, - SVG_LENGTHTYPE_PT, - SVG_LENGTHTYPE_PC, + SVG_LENGTHTYPE_UNKNOWN, + SVG_LENGTHTYPE_NUMBER, + SVG_LENGTHTYPE_PERCENTAGE, + SVG_LENGTHTYPE_EMS, + SVG_LENGTHTYPE_EXS, + SVG_LENGTHTYPE_PX, + SVG_LENGTHTYPE_CM, + SVG_LENGTHTYPE_MM, + SVG_LENGTHTYPE_IN, + SVG_LENGTHTYPE_PT, + SVG_LENGTHTYPE_PC, }; @interface RNSVGLength : NSObject @@ -23,9 +23,9 @@ typedef CF_ENUM(unsigned short, RNSVGLengthUnitType) { @property (nonatomic, assign) CGFloat value; @property (nonatomic, assign) RNSVGLengthUnitType unit; -+ (instancetype) lengthWithNumber: (CGFloat) number; -+ (instancetype) lengthWithString: (NSString *) lengthString; -- (BOOL) isEqualTo: (RNSVGLength *)other; ++ (instancetype)lengthWithNumber:(CGFloat)number; ++ (instancetype)lengthWithString:(NSString *)lengthString; +- (BOOL)isEqualTo:(RNSVGLength *)other; @end diff --git a/apple/Utils/RNSVGLength.mm b/apple/Utils/RNSVGLength.mm index 369258ca..d6eb7a0b 100644 --- a/apple/Utils/RNSVGLength.mm +++ b/apple/Utils/RNSVGLength.mm @@ -1,75 +1,76 @@ -#import #import "RNSVGLength.h" +#import @implementation RNSVGLength -- (instancetype) init +- (instancetype)init { - self = [super init]; - if (self) - { - _value = 0; - _unit = SVG_LENGTHTYPE_UNKNOWN; - } - return self; + self = [super init]; + if (self) { + _value = 0; + _unit = SVG_LENGTHTYPE_UNKNOWN; + } + return self; } -+ (instancetype) lengthWithNumber:(CGFloat)number ++ (instancetype)lengthWithNumber:(CGFloat)number { - RNSVGLength *length = [[self alloc] init]; - length.unit = SVG_LENGTHTYPE_NUMBER; - length.value = number; - return length; + RNSVGLength *length = [[self alloc] init]; + length.unit = SVG_LENGTHTYPE_NUMBER; + length.value = number; + return length; } -+ (instancetype) lengthWithString: (NSString *) lengthString { - NSString *length = [lengthString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - NSUInteger stringLength = [length length]; - NSInteger percentIndex = stringLength - 1; - RNSVGLength *output = [RNSVGLength alloc]; - if (stringLength == 0) { - output.unit = SVG_LENGTHTYPE_UNKNOWN; - output.value = 0; - return output; - } else if ([length characterAtIndex:percentIndex] == '%') { - output.unit = SVG_LENGTHTYPE_PERCENTAGE; - output.value = (CGFloat)[[length substringWithRange:NSMakeRange(0, percentIndex)] doubleValue]; - } else { - NSInteger twoLetterUnitIndex = stringLength - 2; - RNSVGLengthUnitType unit = SVG_LENGTHTYPE_NUMBER; - if (twoLetterUnitIndex > 0) { - NSString *lastTwo = [length substringFromIndex:twoLetterUnitIndex]; - NSUInteger end = twoLetterUnitIndex; - if ([lastTwo isEqualToString:@"px"]) { - unit = SVG_LENGTHTYPE_PX; - } else if ([lastTwo isEqualToString:@"em"]) { - unit = SVG_LENGTHTYPE_EMS; - } else if ([lastTwo isEqualToString:@"ex"]) { - unit = SVG_LENGTHTYPE_EXS; - } else if ([lastTwo isEqualToString:@"pt"]) { - unit = SVG_LENGTHTYPE_PT; - } else if ([lastTwo isEqualToString:@"pc"]) { - unit = SVG_LENGTHTYPE_PC; - } else if ([lastTwo isEqualToString:@"mm"]) { - unit = SVG_LENGTHTYPE_MM; - } else if ([lastTwo isEqualToString:@"cm"]) { - unit = SVG_LENGTHTYPE_CM; - } else if ([lastTwo isEqualToString:@"in"]) { - unit = SVG_LENGTHTYPE_IN; - } else { - end = stringLength; - } - output.value = (CGFloat)[[length substringWithRange:NSMakeRange(0, end)] doubleValue]; - } else { - output.value = (CGFloat)[length doubleValue]; - } - output.unit = unit; - } ++ (instancetype)lengthWithString:(NSString *)lengthString +{ + NSString *length = [lengthString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; + NSUInteger stringLength = [length length]; + NSInteger percentIndex = stringLength - 1; + RNSVGLength *output = [RNSVGLength alloc]; + if (stringLength == 0) { + output.unit = SVG_LENGTHTYPE_UNKNOWN; + output.value = 0; return output; + } else if ([length characterAtIndex:percentIndex] == '%') { + output.unit = SVG_LENGTHTYPE_PERCENTAGE; + output.value = (CGFloat)[[length substringWithRange:NSMakeRange(0, percentIndex)] doubleValue]; + } else { + NSInteger twoLetterUnitIndex = stringLength - 2; + RNSVGLengthUnitType unit = SVG_LENGTHTYPE_NUMBER; + if (twoLetterUnitIndex > 0) { + NSString *lastTwo = [length substringFromIndex:twoLetterUnitIndex]; + NSUInteger end = twoLetterUnitIndex; + if ([lastTwo isEqualToString:@"px"]) { + unit = SVG_LENGTHTYPE_PX; + } else if ([lastTwo isEqualToString:@"em"]) { + unit = SVG_LENGTHTYPE_EMS; + } else if ([lastTwo isEqualToString:@"ex"]) { + unit = SVG_LENGTHTYPE_EXS; + } else if ([lastTwo isEqualToString:@"pt"]) { + unit = SVG_LENGTHTYPE_PT; + } else if ([lastTwo isEqualToString:@"pc"]) { + unit = SVG_LENGTHTYPE_PC; + } else if ([lastTwo isEqualToString:@"mm"]) { + unit = SVG_LENGTHTYPE_MM; + } else if ([lastTwo isEqualToString:@"cm"]) { + unit = SVG_LENGTHTYPE_CM; + } else if ([lastTwo isEqualToString:@"in"]) { + unit = SVG_LENGTHTYPE_IN; + } else { + end = stringLength; + } + output.value = (CGFloat)[[length substringWithRange:NSMakeRange(0, end)] doubleValue]; + } else { + output.value = (CGFloat)[length doubleValue]; + } + output.unit = unit; + } + return output; } -- (BOOL) isEqualTo: (RNSVGLength *)other { - return self.unit == other.unit && self.value == other.value; +- (BOOL)isEqualTo:(RNSVGLength *)other +{ + return self.unit == other.unit && self.value == other.value; } @end diff --git a/apple/Utils/RNSVGMarkerPosition.h b/apple/Utils/RNSVGMarkerPosition.h index 2f1b71cc..831fcfbe 100644 --- a/apple/Utils/RNSVGMarkerPosition.h +++ b/apple/Utils/RNSVGMarkerPosition.h @@ -2,11 +2,7 @@ #import "RNSVGUIKit.h" -typedef enum RNSVGMarkerType { - kStartMarker, - kMidMarker, - kEndMarker -} RNSVGMarkerType; +typedef enum RNSVGMarkerType { kStartMarker, kMidMarker, kEndMarker } RNSVGMarkerType; #define RNSVGZEROPOINT CGRectZero.origin @@ -18,8 +14,8 @@ typedef enum RNSVGMarkerType { @property (nonatomic, assign) float angle; // Instance creation -+ (instancetype) markerPosition:(RNSVGMarkerType)type origin:(CGPoint)origin angle:(float)angle; ++ (instancetype)markerPosition:(RNSVGMarkerType)type origin:(CGPoint)origin angle:(float)angle; -+ (NSArray*) fromCGPath:(CGPathRef)path; ++ (NSArray *)fromCGPath:(CGPathRef)path; @end diff --git a/apple/Utils/RNSVGMarkerPosition.mm b/apple/Utils/RNSVGMarkerPosition.mm index 86ab028d..7da0e5a4 100644 --- a/apple/Utils/RNSVGMarkerPosition.mm +++ b/apple/Utils/RNSVGMarkerPosition.mm @@ -2,155 +2,161 @@ #import "RNSVGMarkerPosition.h" @implementation RNSVGMarkerPosition -- (instancetype) init +- (instancetype)init { - self = [super init]; - if (self) - { - _type = kStartMarker; - _origin = RNSVGZEROPOINT; - _angle = 0; - } - return self; + self = [super init]; + if (self) { + _type = kStartMarker; + _origin = RNSVGZEROPOINT; + _angle = 0; + } + return self; } -+ (instancetype) markerPosition:(RNSVGMarkerType)type origin:(CGPoint)origin angle:(float)angle { - RNSVGMarkerPosition *newElement = [[self alloc] init]; - newElement.type = type; - newElement.origin = origin; - newElement.angle = angle; - return newElement; ++ (instancetype)markerPosition:(RNSVGMarkerType)type origin:(CGPoint)origin angle:(float)angle +{ + RNSVGMarkerPosition *newElement = [[self alloc] init]; + newElement.type = type; + newElement.origin = origin; + newElement.angle = angle; + return newElement; } -+ (NSArray*) fromCGPath:(CGPathRef)path { - positions_ = [[NSMutableArray alloc] init]; - element_index_ = 0; - origin_ = RNSVGZEROPOINT; - subpath_start_ = RNSVGZEROPOINT; - CGPathApply(path, (__bridge void *)positions_, UpdateFromPathElement); - PathIsDone(); - return positions_; ++ (NSArray *)fromCGPath:(CGPathRef)path +{ + positions_ = [[NSMutableArray alloc] init]; + element_index_ = 0; + origin_ = RNSVGZEROPOINT; + subpath_start_ = RNSVGZEROPOINT; + CGPathApply(path, (__bridge void *)positions_, UpdateFromPathElement); + PathIsDone(); + return positions_; } -void PathIsDone() { - float angle = CurrentAngle(kEndMarker); - [positions_ addObject:[RNSVGMarkerPosition markerPosition:kEndMarker origin:origin_ angle:angle]]; +void PathIsDone() +{ + float angle = CurrentAngle(kEndMarker); + [positions_ addObject:[RNSVGMarkerPosition markerPosition:kEndMarker origin:origin_ angle:angle]]; } -static double BisectingAngle(double in_angle, double out_angle) { - // WK193015: Prevent bugs due to angles being non-continuous. - if (fabs(in_angle - out_angle) > 180) - in_angle += 360; - return (in_angle + out_angle) / 2; +static double BisectingAngle(double in_angle, double out_angle) +{ + // WK193015: Prevent bugs due to angles being non-continuous. + if (fabs(in_angle - out_angle) > 180) + in_angle += 360; + return (in_angle + out_angle) / 2; } static CGFloat RNSVG_radToDeg = 180 / (CGFloat)M_PI; -double rad2deg(CGFloat rad) { - return rad * RNSVG_radToDeg; +double rad2deg(CGFloat rad) +{ + return rad * RNSVG_radToDeg; } -CGFloat SlopeAngleRadians(CGSize p) { - CGFloat angle = atan2(p.height, p.width); - return angle; +CGFloat SlopeAngleRadians(CGSize p) +{ + CGFloat angle = atan2(p.height, p.width); + return angle; } -double CurrentAngle(RNSVGMarkerType type) { - // For details of this calculation, see: - // http://www.w3.org/TR/SVG/single-page.html#painting-MarkerElement - double in_angle = rad2deg(SlopeAngleRadians(in_slope_)); - double out_angle = rad2deg(SlopeAngleRadians(out_slope_)); - switch (type) { - case kStartMarker: - if (auto_start_reverse_) - out_angle += 180; - return out_angle; - case kMidMarker: - return BisectingAngle(in_angle, out_angle); - case kEndMarker: - return in_angle; - } - return 0; +double CurrentAngle(RNSVGMarkerType type) +{ + // For details of this calculation, see: + // http://www.w3.org/TR/SVG/single-page.html#painting-MarkerElement + double in_angle = rad2deg(SlopeAngleRadians(in_slope_)); + double out_angle = rad2deg(SlopeAngleRadians(out_slope_)); + switch (type) { + case kStartMarker: + if (auto_start_reverse_) + out_angle += 180; + return out_angle; + case kMidMarker: + return BisectingAngle(in_angle, out_angle); + case kEndMarker: + return in_angle; + } + return 0; } typedef struct SegmentData { - CGSize start_tangent; // Tangent in the start point of the segment. - CGSize end_tangent; // Tangent in the end point of the segment. - CGPoint position; // The end point of the segment. + CGSize start_tangent; // Tangent in the start point of the segment. + CGSize end_tangent; // Tangent in the end point of the segment. + CGPoint position; // The end point of the segment. } SegmentData; -CGSize subtract(CGPoint* p1, CGPoint* p2) { - return CGSizeMake(p2->x - p1->x, p2->y - p1->y); +CGSize subtract(CGPoint *p1, CGPoint *p2) +{ + return CGSizeMake(p2->x - p1->x, p2->y - p1->y); } -static void ComputeQuadTangents(SegmentData* data, - CGPoint* start, - CGPoint* control, - CGPoint* end) { - data->start_tangent = subtract(control, start); - data->end_tangent = subtract(end, control); - if (CGSizeEqualToSize(CGSizeZero, data->start_tangent)) - data->start_tangent = data->end_tangent; - else if (CGSizeEqualToSize(CGSizeZero, data->end_tangent)) - data->end_tangent = data->start_tangent; +static void ComputeQuadTangents(SegmentData *data, CGPoint *start, CGPoint *control, CGPoint *end) +{ + data->start_tangent = subtract(control, start); + data->end_tangent = subtract(end, control); + if (CGSizeEqualToSize(CGSizeZero, data->start_tangent)) + data->start_tangent = data->end_tangent; + else if (CGSizeEqualToSize(CGSizeZero, data->end_tangent)) + data->end_tangent = data->start_tangent; } -SegmentData ExtractPathElementFeatures(const CGPathElement* element) { - SegmentData data; - CGPoint* points = element->points; - switch (element->type) { - case kCGPathElementAddCurveToPoint: - data.position = points[2]; - data.start_tangent = subtract(&points[0], &origin_); - data.end_tangent = subtract(&points[2], &points[1]); - if (CGSizeEqualToSize(CGSizeZero, data.start_tangent)) - ComputeQuadTangents(&data, &points[0], &points[1], &points[2]); - else if (CGSizeEqualToSize(CGSizeZero, data.end_tangent)) - ComputeQuadTangents(&data, &origin_, &points[0], &points[1]); - break; - case kCGPathElementAddQuadCurveToPoint: - data.position = points[1]; - ComputeQuadTangents(&data, &origin_, &points[0], &points[1]); - break; - case kCGPathElementMoveToPoint: - case kCGPathElementAddLineToPoint: - data.position = points[0]; - data.start_tangent = subtract(&data.position, &origin_); - data.end_tangent = subtract(&data.position, &origin_); - break; - case kCGPathElementCloseSubpath: - data.position = subpath_start_; - data.start_tangent = subtract(&data.position, &origin_); - data.end_tangent = subtract(&data.position, &origin_); - break; - } - return data; +SegmentData ExtractPathElementFeatures(const CGPathElement *element) +{ + SegmentData data; + CGPoint *points = element->points; + switch (element->type) { + case kCGPathElementAddCurveToPoint: + data.position = points[2]; + data.start_tangent = subtract(&points[0], &origin_); + data.end_tangent = subtract(&points[2], &points[1]); + if (CGSizeEqualToSize(CGSizeZero, data.start_tangent)) + ComputeQuadTangents(&data, &points[0], &points[1], &points[2]); + else if (CGSizeEqualToSize(CGSizeZero, data.end_tangent)) + ComputeQuadTangents(&data, &origin_, &points[0], &points[1]); + break; + case kCGPathElementAddQuadCurveToPoint: + data.position = points[1]; + ComputeQuadTangents(&data, &origin_, &points[0], &points[1]); + break; + case kCGPathElementMoveToPoint: + case kCGPathElementAddLineToPoint: + data.position = points[0]; + data.start_tangent = subtract(&data.position, &origin_); + data.end_tangent = subtract(&data.position, &origin_); + break; + case kCGPathElementCloseSubpath: + data.position = subpath_start_; + data.start_tangent = subtract(&data.position, &origin_); + data.end_tangent = subtract(&data.position, &origin_); + break; + } + return data; } -void UpdateFromPathElement(void *info, const CGPathElement *element) { - SegmentData segment_data = ExtractPathElementFeatures(element); - // First update the outgoing slope for the previous element. - out_slope_ = segment_data.start_tangent; - // Record the marker for the previous element. - if (element_index_ > 0) { - RNSVGMarkerType marker_type = - element_index_ == 1 ? kStartMarker : kMidMarker; - float angle = CurrentAngle(marker_type); - [positions_ addObject:[RNSVGMarkerPosition markerPosition:marker_type origin:origin_ angle:angle]]; - } - // Update the incoming slope for this marker position. - in_slope_ = segment_data.end_tangent; - // Update marker position. - origin_ = segment_data.position; - // If this is a 'move to' segment, save the point for use with 'close'. - if (element->type == kCGPathElementMoveToPoint) - subpath_start_ = element->points[0]; - else if (element->type == kCGPathElementCloseSubpath) - subpath_start_ = CGPointZero; - ++element_index_; +void UpdateFromPathElement(void *info, const CGPathElement *element) +{ + SegmentData segment_data = ExtractPathElementFeatures(element); + // First update the outgoing slope for the previous element. + out_slope_ = segment_data.start_tangent; + // Record the marker for the previous element. + if (element_index_ > 0) { + RNSVGMarkerType marker_type = element_index_ == 1 ? kStartMarker : kMidMarker; + float angle = CurrentAngle(marker_type); + [positions_ addObject:[RNSVGMarkerPosition markerPosition:marker_type origin:origin_ angle:angle]]; + } + // Update the incoming slope for this marker position. + in_slope_ = segment_data.end_tangent; + // Update marker position. + origin_ = segment_data.position; + // If this is a 'move to' segment, save the point for use with 'close'. + if (element->type == kCGPathElementMoveToPoint) + subpath_start_ = element->points[0]; + else if (element->type == kCGPathElementCloseSubpath) + subpath_start_ = CGPointZero; + ++element_index_; } -NSMutableArray* positions_; +NSMutableArray *positions_; unsigned element_index_; CGPoint origin_; CGPoint subpath_start_; diff --git a/apple/Utils/RNSVGPathMeasure.mm b/apple/Utils/RNSVGPathMeasure.mm index 7fe3085a..76bf1672 100644 --- a/apple/Utils/RNSVGPathMeasure.mm +++ b/apple/Utils/RNSVGPathMeasure.mm @@ -6,7 +6,10 @@ ## License - Creative Commons License
This work is licensed under a Creative Commons Attribution 3.0 United States License. + Creative Commons License
This work is licensed + under a Creative Commons Attribution 3.0 United + States License. For attribution, please include: @@ -22,9 +25,9 @@ static CGFloat idealFlatness = (CGFloat).01; */ static CGFloat distance(CGPoint p1, CGPoint p2) { - CGFloat dx = p2.x - p1.x; - CGFloat dy = p2.y - p1.y; - return hypot(dx, dy); + CGFloat dx = p2.x - p1.x; + CGFloat dy = p2.y - p1.y; + return hypot(dx, dy); } // Subdivide a Bézier (specific division) @@ -60,168 +63,164 @@ static CGFloat distance(CGPoint p1, CGPoint p2) */ static void subdivideBezierAtT(const CGPoint bez[4], CGPoint bez1[4], CGPoint bez2[4], CGFloat t) { - CGPoint q; - CGFloat mt = 1 - t; + CGPoint q; + CGFloat mt = 1 - t; - bez1[0].x = bez[0].x; - bez1[0].y = bez[0].y; - bez2[3].x = bez[3].x; - bez2[3].y = bez[3].y; + bez1[0].x = bez[0].x; + bez1[0].y = bez[0].y; + bez2[3].x = bez[3].x; + bez2[3].y = bez[3].y; - q.x = mt * bez[1].x + t * bez[2].x; - q.y = mt * bez[1].y + t * bez[2].y; - bez1[1].x = mt * bez[0].x + t * bez[1].x; - bez1[1].y = mt * bez[0].y + t * bez[1].y; - bez2[2].x = mt * bez[2].x + t * bez[3].x; - bez2[2].y = mt * bez[2].y + t * bez[3].y; + q.x = mt * bez[1].x + t * bez[2].x; + q.y = mt * bez[1].y + t * bez[2].y; + bez1[1].x = mt * bez[0].x + t * bez[1].x; + bez1[1].y = mt * bez[0].y + t * bez[1].y; + bez2[2].x = mt * bez[2].x + t * bez[3].x; + bez2[2].y = mt * bez[2].y + t * bez[3].y; - bez1[2].x = mt * bez1[1].x + t * q.x; - bez1[2].y = mt * bez1[1].y + t * q.y; - bez2[1].x = mt * q.x + t * bez2[2].x; - bez2[1].y = mt * q.y + t * bez2[2].y; + bez1[2].x = mt * bez1[1].x + t * q.x; + bez1[2].y = mt * bez1[1].y + t * q.y; + bez2[1].x = mt * q.x + t * bez2[2].x; + bez2[1].y = mt * q.y + t * bez2[2].y; - bez1[3].x = bez2[0].x = mt * bez1[2].x + t * bez2[1].x; - bez1[3].y = bez2[0].y = mt * bez1[2].y + t * bez2[1].y; + bez1[3].x = bez2[0].x = mt * bez1[2].x + t * bez2[1].x; + bez1[3].y = bez2[0].y = mt * bez1[2].y + t * bez2[1].y; } @implementation RNSVGPathMeasure -- (void)addLine:(CGPoint *)last next:(const CGPoint *)next { - NSArray *line = @[[NSValue valueWithCGPoint:*last], [NSValue valueWithCGPoint:*next]]; - _pathLength += distance(*last, *next); - [_lengths addObject:[NSNumber numberWithDouble:_pathLength]]; - [_lines addObject:line]; - *last = *next; +- (void)addLine:(CGPoint *)last next:(const CGPoint *)next +{ + NSArray *line = @[ [NSValue valueWithCGPoint:*last], [NSValue valueWithCGPoint:*next] ]; + _pathLength += distance(*last, *next); + [_lengths addObject:[NSNumber numberWithDouble:_pathLength]]; + [_lines addObject:line]; + *last = *next; } -- (void)reset { - _lengths = nil; - _lines = nil; - _isClosed = NO; - _lineCount = 0; - _pathLength = 0; - _path = nil; +- (void)reset +{ + _lengths = nil; + _lines = nil; + _isClosed = NO; + _lineCount = 0; + _pathLength = 0; + _path = nil; } -- (void)extractPathData:(CGPathRef)path { - if (path == _path) { - return; - } - _path = path; - CGPoint origin = CGPointMake (0.0, 0.0); - CGPoint last = CGPointMake (0.0, 0.0); - _lengths = [NSMutableArray array]; - _lines = [NSMutableArray array]; - _isClosed = NO; - _lineCount = 0; - _pathLength = 0; - NSArray *elements = [RNSVGBezierElement elementsFromCGPath:path]; - for (RNSVGBezierElement *element in elements) { - switch (element.elementType) - { - case kCGPathElementMoveToPoint: - origin = last = element.point; - break; +- (void)extractPathData:(CGPathRef)path +{ + if (path == _path) { + return; + } + _path = path; + CGPoint origin = CGPointMake(0.0, 0.0); + CGPoint last = CGPointMake(0.0, 0.0); + _lengths = [NSMutableArray array]; + _lines = [NSMutableArray array]; + _isClosed = NO; + _lineCount = 0; + _pathLength = 0; + NSArray *elements = [RNSVGBezierElement elementsFromCGPath:path]; + for (RNSVGBezierElement *element in elements) { + switch (element.elementType) { + case kCGPathElementMoveToPoint: + origin = last = element.point; + break; - case kCGPathElementAddLineToPoint: { - CGPoint next = element.point; - [self addLine:&last next:&next]; - _lineCount++; - break; - } - case kCGPathElementAddQuadCurveToPoint: - case kCGPathElementAddCurveToPoint: - { - // handle both curve types gracefully - CGPoint curveTo = element.point; - CGPoint ctrl1 = element.controlPoint1; - CGPoint ctrl2 = element.elementType == kCGPathElementAddQuadCurveToPoint ? ctrl1 : element.controlPoint2; + case kCGPathElementAddLineToPoint: { + CGPoint next = element.point; + [self addLine:&last next:&next]; + _lineCount++; + break; + } + case kCGPathElementAddQuadCurveToPoint: + case kCGPathElementAddCurveToPoint: { + // handle both curve types gracefully + CGPoint curveTo = element.point; + CGPoint ctrl1 = element.controlPoint1; + CGPoint ctrl2 = element.elementType == kCGPathElementAddQuadCurveToPoint ? ctrl1 : element.controlPoint2; - // this is the bezier for our current element - CGPoint bezier[4] = { last, ctrl1, ctrl2, curveTo }; - NSValue *arr = [NSValue valueWithBytes:&bezier objCType:@encode(CGPoint[4])]; - NSMutableArray *curves = [NSMutableArray arrayWithObjects:arr, nil]; + // this is the bezier for our current element + CGPoint bezier[4] = {last, ctrl1, ctrl2, curveTo}; + NSValue *arr = [NSValue valueWithBytes:&bezier objCType:@encode(CGPoint[4])]; + NSMutableArray *curves = [NSMutableArray arrayWithObjects:arr, nil]; - for (NSInteger curveIndex = 0; curveIndex >= 0; curveIndex--) { - CGPoint bez[4]; - [curves[curveIndex] getValue:&bez]; - [curves removeLastObject]; + for (NSInteger curveIndex = 0; curveIndex >= 0; curveIndex--) { + CGPoint bez[4]; + [curves[curveIndex] getValue:&bez]; + [curves removeLastObject]; - // calculate the error rate of the curve vs - // a line segment between the start and end points - CGPoint ctrl1 = bez[1]; - CGPoint ctrl2 = bez[2]; - CGPoint next = bez[3]; - CGFloat polyLen = - distance(last, ctrl1) + - distance(ctrl1, ctrl2) + - distance(ctrl2, next); - CGFloat chordLen = distance(last, next); - CGFloat error = polyLen - chordLen; + // calculate the error rate of the curve vs + // a line segment between the start and end points + CGPoint ctrl1 = bez[1]; + CGPoint ctrl2 = bez[2]; + CGPoint next = bez[3]; + CGFloat polyLen = distance(last, ctrl1) + distance(ctrl1, ctrl2) + distance(ctrl2, next); + CGFloat chordLen = distance(last, next); + CGFloat error = polyLen - chordLen; - // if the error is less than our accepted level of error - // then add a line, else, split the curve in half - if (error <= idealFlatness) { - [self addLine:&last next:&next]; - _lineCount++; - } else { - CGPoint bez1[4], bez2[4]; - subdivideBezierAtT(bez, bez1, bez2, .5); - [curves addObject:[NSValue valueWithBytes:&bez2 objCType:@encode(CGPoint[4])]]; - [curves addObject:[NSValue valueWithBytes:&bez1 objCType:@encode(CGPoint[4])]]; - curveIndex += 2; - } - } - break; - } - - case kCGPathElementCloseSubpath: { - CGPoint next = origin; - [self addLine:&last next:&next]; - _lineCount++; - _isClosed = YES; - break; - } - - default: - break; + // if the error is less than our accepted level of error + // then add a line, else, split the curve in half + if (error <= idealFlatness) { + [self addLine:&last next:&next]; + _lineCount++; + } else { + CGPoint bez1[4], bez2[4]; + subdivideBezierAtT(bez, bez1, bez2, .5); + [curves addObject:[NSValue valueWithBytes:&bez2 objCType:@encode(CGPoint[4])]]; + [curves addObject:[NSValue valueWithBytes:&bez1 objCType:@encode(CGPoint[4])]]; + curveIndex += 2; + } } + break; + } + + case kCGPathElementCloseSubpath: { + CGPoint next = origin; + [self addLine:&last next:&next]; + _lineCount++; + _isClosed = YES; + break; + } + + default: + break; } + } } -- (void)getPosAndTan:(CGFloat *)angle midPoint:(CGFloat)midPoint x:(CGFloat *)x y:(CGFloat *)y { - // Investigation suggests binary search is faster at lineCount >= 16 - // https://gist.github.com/msand/4c7993319425f9d7933be58ad9ada1a4 - NSUInteger i = _lineCount < 16 ? - [_lengths - indexOfObjectPassingTest:^(NSNumber* length, NSUInteger index, BOOL * _Nonnull stop) { - BOOL contains = midPoint <= [length doubleValue]; - return contains; - }] - : - [_lengths - indexOfObject:[NSNumber numberWithDouble:midPoint] - inSortedRange:NSMakeRange(0, _lineCount) - options:NSBinarySearchingInsertionIndex - usingComparator:^(NSNumber* obj1, NSNumber* obj2) { - return [obj1 compare:obj2]; - }]; +- (void)getPosAndTan:(CGFloat *)angle midPoint:(CGFloat)midPoint x:(CGFloat *)x y:(CGFloat *)y +{ + // Investigation suggests binary search is faster at lineCount >= 16 + // https://gist.github.com/msand/4c7993319425f9d7933be58ad9ada1a4 + NSUInteger i = _lineCount < 16 + ? [_lengths indexOfObjectPassingTest:^(NSNumber *length, NSUInteger index, BOOL *_Nonnull stop) { + BOOL contains = midPoint <= [length doubleValue]; + return contains; + }] + : [_lengths indexOfObject:[NSNumber numberWithDouble:midPoint] + inSortedRange:NSMakeRange(0, _lineCount) + options:NSBinarySearchingInsertionIndex + usingComparator:^(NSNumber *obj1, NSNumber *obj2) { + return [obj1 compare:obj2]; + }]; - CGFloat totalLength = (CGFloat)[_lengths[i] doubleValue]; - CGFloat prevLength = i == 0 ? 0 : (CGFloat)[_lengths[i - 1] doubleValue]; + CGFloat totalLength = (CGFloat)[_lengths[i] doubleValue]; + CGFloat prevLength = i == 0 ? 0 : (CGFloat)[_lengths[i - 1] doubleValue]; - CGFloat length = totalLength - prevLength; - CGFloat percent = (midPoint - prevLength) / length; + CGFloat length = totalLength - prevLength; + CGFloat percent = (midPoint - prevLength) / length; - NSArray * points = [_lines objectAtIndex: i]; - CGPoint p1 = [[points objectAtIndex: 0] CGPointValue]; - CGPoint p2 = [[points objectAtIndex: 1] CGPointValue]; + NSArray *points = [_lines objectAtIndex:i]; + CGPoint p1 = [[points objectAtIndex:0] CGPointValue]; + CGPoint p2 = [[points objectAtIndex:1] CGPointValue]; - CGFloat ldx = p2.x - p1.x; - CGFloat ldy = p2.y - p1.y; - *angle = atan2(ldy, ldx); - *x = p1.x + ldx * percent; - *y = p1.y + ldy * percent; + CGFloat ldx = p2.x - p1.x; + CGFloat ldy = p2.y - p1.y; + *angle = atan2(ldy, ldx); + *x = p1.x + ldx * percent; + *y = p1.y + ldy * percent; } @end diff --git a/apple/Utils/RNSVGPathParser.h b/apple/Utils/RNSVGPathParser.h index 2e0c310d..c7a67a43 100644 --- a/apple/Utils/RNSVGPathParser.h +++ b/apple/Utils/RNSVGPathParser.h @@ -10,7 +10,7 @@ @interface RNSVGPathParser : NSObject -- (instancetype) initWithPathString:(NSString *)d; +- (instancetype)initWithPathString:(NSString *)d; - (CGPathRef)getPath; @end diff --git a/apple/Utils/RNSVGPathParser.mm b/apple/Utils/RNSVGPathParser.mm index 56a472ca..4805b9c7 100644 --- a/apple/Utils/RNSVGPathParser.mm +++ b/apple/Utils/RNSVGPathParser.mm @@ -11,31 +11,30 @@ #import #import "RNSVGBezierElement.h" -@implementation RNSVGPathParser -{ - char prev_cmd; - NSUInteger i; - NSUInteger l; - NSString* s; - float _penX; - float _penY; - float _penDownX; - float _penDownY; - float _pivotX; - float _pivotY; - BOOL _valid; - BOOL _penDownSet; +@implementation RNSVGPathParser { + char prev_cmd; + NSUInteger i; + NSUInteger l; + NSString *s; + float _penX; + float _penY; + float _penDownX; + float _penDownY; + float _pivotX; + float _pivotY; + BOOL _valid; + BOOL _penDownSet; } - (instancetype)initWithPathString:(NSString *)d { - if (self = [super init]) { - prev_cmd = ' '; - l = [d length]; - i = 0; - s = d; - } - return self; + if (self = [super init]) { + prev_cmd = ' '; + l = [d length]; + i = 0; + s = d; + } + return self; } #define NEXT_FLOAT [self parse_list_number] @@ -43,553 +42,615 @@ - (CGPathRef)getPath { - CGMutablePathRef path = CGPathCreateMutable(); - while (i < l) { - [self skip_spaces]; - - if (i >= l) { - break; - } - - bool has_prev_cmd = prev_cmd != ' '; - char first_char = [s characterAtIndex:i]; - - if (!has_prev_cmd && first_char != 'M' && first_char != 'm') { - // The first segment must be a MoveTo. - RCTLogError(@"UnexpectedData: %@", s); - CGPathRelease(path); - return nil; - } - - // TODO: simplify - bool is_implicit_move_to = false; - char cmd = ' '; - if ([self is_cmd:first_char]) { - is_implicit_move_to = false; - cmd = first_char; - i += 1; - } else if ([self is_number_start:first_char] && has_prev_cmd) { - if (prev_cmd == 'Z' || prev_cmd == 'z') { - // ClosePath cannot be followed by a number. - RCTLogError(@"UnexpectedData: %@", s); - CGPathRelease(path); - return nil; - } - - if (prev_cmd == 'M' || prev_cmd == 'm') { - // 'If a moveto is followed by multiple pairs of coordinates, - // the subsequent pairs are treated as implicit lineto commands.' - // So we parse them as LineTo. - is_implicit_move_to = true; - if ([self is_absolute:prev_cmd]) { - cmd = 'L'; - } else { - cmd = 'l'; - } - } else { - is_implicit_move_to = false; - cmd = prev_cmd; - } - } else { - RCTLogError(@"UnexpectedData: %@", s); - CGPathRelease(path); - return nil; - } - - bool absolute = [self is_absolute:cmd]; - switch (cmd) { - case 'm': { - [self move:path x:NEXT_FLOAT y:NEXT_FLOAT]; - break; - } - case 'M': { - [self moveTo:path x:NEXT_FLOAT y:NEXT_FLOAT]; - break; - } - case 'l': { - [self line:path x:NEXT_FLOAT y:NEXT_FLOAT]; - break; - } - case 'L': { - [self lineTo:path x:NEXT_FLOAT y:NEXT_FLOAT]; - break; - } - case 'h': { - [self line:path x:NEXT_FLOAT y:0]; - break; - } - case 'H': { - [self lineTo:path x:NEXT_FLOAT y:_penY]; - break; - } - case 'v': { - [self line:path x:0 y:NEXT_FLOAT]; - break; - } - case 'V': { - [self lineTo:path x:_penX y:NEXT_FLOAT]; - break; - } - case 'c': { - [self curve:path c1x:NEXT_FLOAT c1y:NEXT_FLOAT c2x:NEXT_FLOAT c2y:NEXT_FLOAT ex:NEXT_FLOAT ey:NEXT_FLOAT]; - break; - } - case 'C': { - [self curveTo:path c1x:NEXT_FLOAT c1y:NEXT_FLOAT c2x:NEXT_FLOAT c2y:NEXT_FLOAT ex:NEXT_FLOAT ey:NEXT_FLOAT]; - break; - } - case 's': { - [self smoothCurve:path c1x:NEXT_FLOAT c1y:NEXT_FLOAT ex:NEXT_FLOAT ey:NEXT_FLOAT]; - break; - } - case 'S': { - [self smoothCurveTo:path c1x:NEXT_FLOAT c1y:NEXT_FLOAT ex:NEXT_FLOAT ey:NEXT_FLOAT]; - break; - } - case 'q': { - [self quadraticBezierCurve:path c1x:NEXT_FLOAT c1y:NEXT_FLOAT c2x:NEXT_FLOAT c2y:NEXT_FLOAT]; - break; - } - case 'Q': { - [self quadraticBezierCurveTo:path c1x:NEXT_FLOAT c1y:NEXT_FLOAT c2x:NEXT_FLOAT c2y:NEXT_FLOAT]; - break; - } - case 't': { - [self smoothQuadraticBezierCurve:path c1x:NEXT_FLOAT c1y:NEXT_FLOAT]; - break; - } - case 'T': { - [self smoothQuadraticBezierCurveTo:path c1x:NEXT_FLOAT c1y:NEXT_FLOAT]; - break; - } - case 'a': { - [self arc:path rx:NEXT_FLOAT ry:NEXT_FLOAT rotation:NEXT_FLOAT outer:NEXT_BOOL clockwise:NEXT_BOOL x:NEXT_FLOAT y:NEXT_FLOAT]; - break; - } - case 'A': { - [self arcTo:path rx:NEXT_FLOAT ry:NEXT_FLOAT rotation:NEXT_FLOAT outer:NEXT_BOOL clockwise:NEXT_BOOL x:NEXT_FLOAT y:NEXT_FLOAT]; - break; - } - case 'z': - case 'Z': { - [self close:path]; - break; - } - default: { - RCTLogError(@"UnexpectedData: %@", s); - CGPathRelease(path); - return nil; - } - } - - - if (is_implicit_move_to) { - if (absolute) { - prev_cmd = 'M'; - } else { - prev_cmd = 'm'; - } - } else { - prev_cmd = cmd; - } + CGMutablePathRef path = CGPathCreateMutable(); + while (i < l) { + [self skip_spaces]; + if (i >= l) { + break; } - return (CGPathRef)CFAutorelease(path); + bool has_prev_cmd = prev_cmd != ' '; + char first_char = [s characterAtIndex:i]; + + if (!has_prev_cmd && first_char != 'M' && first_char != 'm') { + // The first segment must be a MoveTo. + RCTLogError(@"UnexpectedData: %@", s); + CGPathRelease(path); + return nil; + } + + // TODO: simplify + bool is_implicit_move_to = false; + char cmd = ' '; + if ([self is_cmd:first_char]) { + is_implicit_move_to = false; + cmd = first_char; + i += 1; + } else if ([self is_number_start:first_char] && has_prev_cmd) { + if (prev_cmd == 'Z' || prev_cmd == 'z') { + // ClosePath cannot be followed by a number. + RCTLogError(@"UnexpectedData: %@", s); + CGPathRelease(path); + return nil; + } + + if (prev_cmd == 'M' || prev_cmd == 'm') { + // 'If a moveto is followed by multiple pairs of coordinates, + // the subsequent pairs are treated as implicit lineto commands.' + // So we parse them as LineTo. + is_implicit_move_to = true; + if ([self is_absolute:prev_cmd]) { + cmd = 'L'; + } else { + cmd = 'l'; + } + } else { + is_implicit_move_to = false; + cmd = prev_cmd; + } + } else { + RCTLogError(@"UnexpectedData: %@", s); + CGPathRelease(path); + return nil; + } + + bool absolute = [self is_absolute:cmd]; + switch (cmd) { + case 'm': { + [self move:path x:NEXT_FLOAT y:NEXT_FLOAT]; + break; + } + case 'M': { + [self moveTo:path x:NEXT_FLOAT y:NEXT_FLOAT]; + break; + } + case 'l': { + [self line:path x:NEXT_FLOAT y:NEXT_FLOAT]; + break; + } + case 'L': { + [self lineTo:path x:NEXT_FLOAT y:NEXT_FLOAT]; + break; + } + case 'h': { + [self line:path x:NEXT_FLOAT y:0]; + break; + } + case 'H': { + [self lineTo:path x:NEXT_FLOAT y:_penY]; + break; + } + case 'v': { + [self line:path x:0 y:NEXT_FLOAT]; + break; + } + case 'V': { + [self lineTo:path x:_penX y:NEXT_FLOAT]; + break; + } + case 'c': { + [self curve:path c1x:NEXT_FLOAT c1y:NEXT_FLOAT c2x:NEXT_FLOAT c2y:NEXT_FLOAT ex:NEXT_FLOAT ey:NEXT_FLOAT]; + break; + } + case 'C': { + [self curveTo:path c1x:NEXT_FLOAT c1y:NEXT_FLOAT c2x:NEXT_FLOAT c2y:NEXT_FLOAT ex:NEXT_FLOAT ey:NEXT_FLOAT]; + break; + } + case 's': { + [self smoothCurve:path c1x:NEXT_FLOAT c1y:NEXT_FLOAT ex:NEXT_FLOAT ey:NEXT_FLOAT]; + break; + } + case 'S': { + [self smoothCurveTo:path c1x:NEXT_FLOAT c1y:NEXT_FLOAT ex:NEXT_FLOAT ey:NEXT_FLOAT]; + break; + } + case 'q': { + [self quadraticBezierCurve:path c1x:NEXT_FLOAT c1y:NEXT_FLOAT c2x:NEXT_FLOAT c2y:NEXT_FLOAT]; + break; + } + case 'Q': { + [self quadraticBezierCurveTo:path c1x:NEXT_FLOAT c1y:NEXT_FLOAT c2x:NEXT_FLOAT c2y:NEXT_FLOAT]; + break; + } + case 't': { + [self smoothQuadraticBezierCurve:path c1x:NEXT_FLOAT c1y:NEXT_FLOAT]; + break; + } + case 'T': { + [self smoothQuadraticBezierCurveTo:path c1x:NEXT_FLOAT c1y:NEXT_FLOAT]; + break; + } + case 'a': { + [self arc:path + rx:NEXT_FLOAT + ry:NEXT_FLOAT + rotation:NEXT_FLOAT + outer:NEXT_BOOL + clockwise:NEXT_BOOL + x:NEXT_FLOAT + y:NEXT_FLOAT]; + break; + } + case 'A': { + [self arcTo:path + rx:NEXT_FLOAT + ry:NEXT_FLOAT + rotation:NEXT_FLOAT + outer:NEXT_BOOL + clockwise:NEXT_BOOL + x:NEXT_FLOAT + y:NEXT_FLOAT]; + break; + } + case 'z': + case 'Z': { + [self close:path]; + break; + } + default: { + RCTLogError(@"UnexpectedData: %@", s); + CGPathRelease(path); + return nil; + } + } + + if (is_implicit_move_to) { + if (absolute) { + prev_cmd = 'M'; + } else { + prev_cmd = 'm'; + } + } else { + prev_cmd = cmd; + } + } + + return (CGPathRef)CFAutorelease(path); } - (void)move:(CGMutablePathRef)path x:(float)x y:(float)y { - [self moveTo:path x:x + _penX y:y + _penY]; + [self moveTo:path x:x + _penX y:y + _penY]; } - (void)moveTo:(CGMutablePathRef)path x:(float)x y:(float)y { - //RCTLogInfo(@"move x: %f y: %f", x, y); - _penDownX = _pivotX = _penX = x; - _penDownY = _pivotY = _penY = y; - CGPathMoveToPoint(path, nil, x, y); + // RCTLogInfo(@"move x: %f y: %f", x, y); + _penDownX = _pivotX = _penX = x; + _penDownY = _pivotY = _penY = y; + CGPathMoveToPoint(path, nil, x, y); } - (void)line:(CGMutablePathRef)path x:(float)x y:(float)y { - [self lineTo:path x:x + _penX y:y + _penY]; + [self lineTo:path x:x + _penX y:y + _penY]; } -- (void)lineTo:(CGMutablePathRef)path x:(float)x y:(float)y{ - //RCTLogInfo(@"line x: %f y: %f", x, y); - [self setPenDown]; - _pivotX = _penX = x; - _pivotY = _penY = y; - CGPathAddLineToPoint(path, nil, x, y); -} - -- (void)curve:(CGMutablePathRef)path c1x:(float)c1x c1y:(float)c1y c2x:(float)c2x c2y:(float)c2y ex:(float)ex ey:(float)ey +- (void)lineTo:(CGMutablePathRef)path x:(float)x y:(float)y { - [self curveTo:path c1x:c1x + _penX - c1y:c1y + _penY - c2x:c2x + _penX - c2y:c2y + _penY - ex:ex + _penX - ey:ey + _penY]; + // RCTLogInfo(@"line x: %f y: %f", x, y); + [self setPenDown]; + _pivotX = _penX = x; + _pivotY = _penY = y; + CGPathAddLineToPoint(path, nil, x, y); } -- (void)curveTo:(CGMutablePathRef)path c1x:(float)c1x c1y:(float)c1y c2x:(float)c2x c2y:(float)c2y ex:(float)ex ey:(float)ey +- (void)curve:(CGMutablePathRef)path + c1x:(float)c1x + c1y:(float)c1y + c2x:(float)c2x + c2y:(float)c2y + ex:(float)ex + ey:(float)ey { - //RCTLogInfo(@"curve c1x: %f c1y: %f c2x: %f c2y: %f ex: %f ey: %f", c1x, c1y, c2x, c2y, ex, ey); - _pivotX = c2x; - _pivotY = c2y; - [self curveToPoint:path c1x:(float)c1x c1y:(float)c1y c2x:(float)c2x c2y:(float)c2y ex:(float)ex ey:(float)ey]; + [self curveTo:path c1x:c1x + _penX c1y:c1y + _penY c2x:c2x + _penX c2y:c2y + _penY ex:ex + _penX ey:ey + _penY]; } -- (void)curveToPoint:(CGMutablePathRef)path c1x:(float)c1x c1y:(float)c1y c2x:(float)c2x c2y:(float)c2y ex:(float)ex ey:(float)ey +- (void)curveTo:(CGMutablePathRef)path + c1x:(float)c1x + c1y:(float)c1y + c2x:(float)c2x + c2y:(float)c2y + ex:(float)ex + ey:(float)ey { - [self setPenDown]; - _penX = ex; - _penY = ey; - CGPathAddCurveToPoint(path, nil, c1x, c1y, c2x, c2y, ex, ey); + // RCTLogInfo(@"curve c1x: %f c1y: %f c2x: %f c2y: %f ex: %f ey: %f", c1x, c1y, c2x, c2y, ex, ey); + _pivotX = c2x; + _pivotY = c2y; + [self curveToPoint:path c1x:(float)c1x c1y:(float)c1y c2x:(float)c2x c2y:(float)c2y ex:(float)ex ey:(float)ey]; +} + +- (void)curveToPoint:(CGMutablePathRef)path + c1x:(float)c1x + c1y:(float)c1y + c2x:(float)c2x + c2y:(float)c2y + ex:(float)ex + ey:(float)ey +{ + [self setPenDown]; + _penX = ex; + _penY = ey; + CGPathAddCurveToPoint(path, nil, c1x, c1y, c2x, c2y, ex, ey); } - (void)smoothCurve:(CGMutablePathRef)path c1x:(float)c1x c1y:(float)c1y ex:(float)ex ey:(float)ey { - [self smoothCurveTo:path c1x:c1x + _penX c1y:c1y + _penY ex:ex + _penX ey:ey + _penY]; + [self smoothCurveTo:path c1x:c1x + _penX c1y:c1y + _penY ex:ex + _penX ey:ey + _penY]; } - (void)smoothCurveTo:(CGMutablePathRef)path c1x:(float)c1x c1y:(float)c1y ex:(float)ex ey:(float)ey { - //RCTLogInfo(@"smoothcurve c1x: %f c1y: %f ex: %f ey: %f", c1x, c1y, ex, ey); - float c2x = c1x; - float c2y = c1y; - c1x = (_penX * 2) - _pivotX; - c1y = (_penY * 2) - _pivotY; - _pivotX = c2x; - _pivotY = c2y; - [self curveToPoint:path c1x:(float)c1x c1y:(float)c1y c2x:(float)c2x c2y:(float)c2y ex:(float)ex ey:(float)ey]; + // RCTLogInfo(@"smoothcurve c1x: %f c1y: %f ex: %f ey: %f", c1x, c1y, ex, ey); + float c2x = c1x; + float c2y = c1y; + c1x = (_penX * 2) - _pivotX; + c1y = (_penY * 2) - _pivotY; + _pivotX = c2x; + _pivotY = c2y; + [self curveToPoint:path c1x:(float)c1x c1y:(float)c1y c2x:(float)c2x c2y:(float)c2y ex:(float)ex ey:(float)ey]; } - (void)quadraticBezierCurve:(CGMutablePathRef)path c1x:(float)c1x c1y:(float)c1y c2x:(float)c2x c2y:(float)c2y { - [self quadraticBezierCurveTo:path c1x:(float)c1x + _penX c1y:(float)c1y + _penY c2x:(float)c2x + _penX c2y:(float)c2y + _penY]; + [self quadraticBezierCurveTo:path + c1x:(float)c1x + _penX + c1y:(float)c1y + _penY + c2x:(float)c2x + _penX + c2y:(float)c2y + _penY]; } - (void)quadraticBezierCurveTo:(CGMutablePathRef)path c1x:(float)c1x c1y:(float)c1y c2x:(float)c2x c2y:(float)c2y { - //RCTLogInfo(@"quad c1x: %f c1y: %f c2x: %f c2y: %f", c1x, c1y, c2x, c2y); - _pivotX = c1x; - _pivotY = c1y; - float ex = c2x; - float ey = c2y; - c2x = (ex + c1x * 2) / 3; - c2y = (ey + c1y * 2) / 3; - c1x = (_penX + c1x * 2) / 3; - c1y = (_penY + c1y * 2) / 3; - [self curveToPoint:path c1x:(float)c1x c1y:(float)c1y c2x:(float)c2x c2y:(float)c2y ex:(float)ex ey:(float)ey]; + // RCTLogInfo(@"quad c1x: %f c1y: %f c2x: %f c2y: %f", c1x, c1y, c2x, c2y); + _pivotX = c1x; + _pivotY = c1y; + float ex = c2x; + float ey = c2y; + c2x = (ex + c1x * 2) / 3; + c2y = (ey + c1y * 2) / 3; + c1x = (_penX + c1x * 2) / 3; + c1y = (_penY + c1y * 2) / 3; + [self curveToPoint:path c1x:(float)c1x c1y:(float)c1y c2x:(float)c2x c2y:(float)c2y ex:(float)ex ey:(float)ey]; } - (void)smoothQuadraticBezierCurve:(CGMutablePathRef)path c1x:(float)c1x c1y:(float)c1y { - [self smoothQuadraticBezierCurveTo:path c1x:c1x + _penX c1y:c1y + _penY]; + [self smoothQuadraticBezierCurveTo:path c1x:c1x + _penX c1y:c1y + _penY]; } - (void)smoothQuadraticBezierCurveTo:(CGMutablePathRef)path c1x:(float)c1x c1y:(float)c1y { - //RCTLogInfo(@"smoothquad c1x: %f c1y: %f", c1x, c1y); - float c2x = c1x; - float c2y = c1y; - c1x = (_penX * 2) - _pivotX; - c1y = (_penY * 2) - _pivotY; - [self quadraticBezierCurveTo:path c1x:c1x c1y:c1y c2x:c2x c2y:c2y]; + // RCTLogInfo(@"smoothquad c1x: %f c1y: %f", c1x, c1y); + float c2x = c1x; + float c2y = c1y; + c1x = (_penX * 2) - _pivotX; + c1y = (_penY * 2) - _pivotY; + [self quadraticBezierCurveTo:path c1x:c1x c1y:c1y c2x:c2x c2y:c2y]; } -- (void)arc:(CGMutablePathRef)path rx:(float)rx ry:(float)ry rotation:(float)rotation outer:(BOOL)outer clockwise:(BOOL)clockwise x:(float)x y:(float)y +- (void)arc:(CGMutablePathRef)path + rx:(float)rx + ry:(float)ry + rotation:(float)rotation + outer:(BOOL)outer + clockwise:(BOOL)clockwise + x:(float)x + y:(float)y { - [self arcTo:path rx:rx ry:ry rotation:rotation outer:outer clockwise:clockwise x:x + _penX y:y + _penY]; + [self arcTo:path rx:rx ry:ry rotation:rotation outer:outer clockwise:clockwise x:x + _penX y:y + _penY]; } -- (void)arcTo:(CGMutablePathRef)path rx:(float)rx ry:(float)ry rotation:(float)rotation outer:(BOOL)outer clockwise:(BOOL)clockwise x:(float)x y:(float)y +- (void)arcTo:(CGMutablePathRef)path + rx:(float)rx + ry:(float)ry + rotation:(float)rotation + outer:(BOOL)outer + clockwise:(BOOL)clockwise + x:(float)x + y:(float)y { - //RCTLogInfo(@"arc rx: %f ry: %f rotation: %f outer: %i clockwise: %i x: %f y: %f", rx, ry, rotation, outer, clockwise, x, y); - float tX = _penX; - float tY = _penY; + // RCTLogInfo(@"arc rx: %f ry: %f rotation: %f outer: %i clockwise: %i x: %f y: %f", rx, ry, rotation, outer, + // clockwise, x, y); + float tX = _penX; + float tY = _penY; - ry = fabsf(ry == 0 ? (rx == 0 ? (y - tY) : rx) : ry); - rx = fabsf(rx == 0 ? (x - tX) : rx); + ry = fabsf(ry == 0 ? (rx == 0 ? (y - tY) : rx) : ry); + rx = fabsf(rx == 0 ? (x - tX) : rx); - if (rx == 0 || ry == 0 || (x == tX && y == tY)) { - [self lineTo:path x:x y:y]; - return; + if (rx == 0 || ry == 0 || (x == tX && y == tY)) { + [self lineTo:path x:x y:y]; + return; + } + + float rad = rotation * (float)M_PI / 180; + float cosed = cosf(rad); + float sined = sinf(rad); + x -= tX; + y -= tY; + // Ellipse Center + float cx = cosed * x / 2 + sined * y / 2; + float cy = -sined * x / 2 + cosed * y / 2; + float rxry = rx * rx * ry * ry; + float rycx = ry * ry * cx * cx; + float rxcy = rx * rx * cy * cy; + float a = rxry - rxcy - rycx; + + if (a < 0) { + a = sqrtf(1 - a / rxry); + rx *= a; + ry *= a; + cx = x / 2; + cy = y / 2; + } else { + a = sqrtf(a / (rxcy + rycx)); + + if (outer == clockwise) { + a = -a; } + float cxd = -a * cy * rx / ry; + float cyd = a * cx * ry / rx; + cx = cosed * cxd - sined * cyd + x / 2; + cy = sined * cxd + cosed * cyd + y / 2; + } + // Rotation + Scale Transform + float xx = cosed / rx; + float yx = sined / rx; + float xy = -sined / ry; + float yy = cosed / ry; - float rad = rotation * (float)M_PI / 180; - float cosed = cosf(rad); - float sined = sinf(rad); - x -= tX; - y -= tY; - // Ellipse Center - float cx = cosed * x / 2 + sined * y / 2; - float cy = -sined * x / 2 + cosed * y / 2; - float rxry = rx * rx * ry * ry; - float rycx = ry * ry * cx * cx; - float rxcy = rx * rx * cy * cy; - float a = rxry - rxcy - rycx; + // Start and End Angle + float sa = atan2f(xy * -cx + yy * -cy, xx * -cx + yx * -cy); + float ea = atan2f(xy * (x - cx) + yy * (y - cy), xx * (x - cx) + yx * (y - cy)); - if (a < 0){ - a = sqrtf(1 - a / rxry); - rx *= a; - ry *= a; - cx = x / 2; - cy = y / 2; - } else { - a = sqrtf(a / (rxcy + rycx)); + cx += tX; + cy += tY; + x += tX; + y += tY; - if (outer == clockwise) { - a = -a; - } - float cxd = -a * cy * rx / ry; - float cyd = a * cx * ry / rx; - cx = cosed * cxd - sined * cyd + x / 2; - cy = sined * cxd + cosed * cyd + y / 2; - } + [self setPenDown]; - // Rotation + Scale Transform - float xx = cosed / rx; - float yx = sined / rx; - float xy = -sined / ry; - float yy = cosed / ry; + _penX = _pivotX = x; + _penY = _pivotY = y; - // Start and End Angle - float sa = atan2f(xy * -cx + yy * -cy, xx * -cx + yx * -cy); - float ea = atan2f(xy * (x - cx) + yy * (y - cy), xx * (x - cx) + yx * (y - cy)); - - cx += tX; - cy += tY; - x += tX; - y += tY; - - [self setPenDown]; - - _penX = _pivotX = x; - _penY = _pivotY = y; - - [self arcToBezier:path cx:cx cy:cy rx:rx ry:ry sa:sa ea:ea clockwise:clockwise rad:rad]; + [self arcToBezier:path cx:cx cy:cy rx:rx ry:ry sa:sa ea:ea clockwise:clockwise rad:rad]; } -- (void)arcToBezier:(CGMutablePathRef)path cx:(float)cx cy:(float)cy rx:(float)rx ry:(float)ry sa:(float)sa ea:(float)ea clockwise:(BOOL)clockwise rad:(float)rad +- (void)arcToBezier:(CGMutablePathRef)path + cx:(float)cx + cy:(float)cy + rx:(float)rx + ry:(float)ry + sa:(float)sa + ea:(float)ea + clockwise:(BOOL)clockwise + rad:(float)rad { - // Inverse Rotation + Scale Transform - float cosed = cosf(rad); - float sined = sinf(rad); - float xx = cosed * rx; - float yx = -sined * ry; - float xy = sined * rx; - float yy = cosed * ry; + // Inverse Rotation + Scale Transform + float cosed = cosf(rad); + float sined = sinf(rad); + float xx = cosed * rx; + float yx = -sined * ry; + float xy = sined * rx; + float yy = cosed * ry; - // Bezier Curve Approximation - float arc = ea - sa; - if (arc < 0 && clockwise) { - arc += M_PI * 2; - } else if (arc > 0 && !clockwise) { - arc -= M_PI * 2; - } + // Bezier Curve Approximation + float arc = ea - sa; + if (arc < 0 && clockwise) { + arc += M_PI * 2; + } else if (arc > 0 && !clockwise) { + arc -= M_PI * 2; + } - int n = (int)ceilf(fabsf(arc / ((float)M_PI / 2))); + int n = (int)ceilf(fabsf(arc / ((float)M_PI / 2))); - float step = arc / n; - float k = (4.0f / 3.0f) * tanf(step / 4); + float step = arc / n; + float k = (4.0f / 3.0f) * tanf(step / 4); - float x = cosf(sa); - float y = sinf(sa); + float x = cosf(sa); + float y = sinf(sa); - for (int i = 0; i < n; i++){ - float cp1x = x - k * y; - float cp1y = y + k * x; + for (int i = 0; i < n; i++) { + float cp1x = x - k * y; + float cp1y = y + k * x; - sa += step; - x = cosf(sa); - y = sinf(sa); + sa += step; + x = cosf(sa); + y = sinf(sa); - float cp2x = x + k * y; - float cp2y = y - k * x; + float cp2x = x + k * y; + float cp2y = y - k * x; - float c1x = cx + xx * cp1x + yx * cp1y; - float c1y = cy + xy * cp1x + yy * cp1y; - float c2x = cx + xx * cp2x + yx * cp2y; - float c2y = cy + xy * cp2x + yy * cp2y; - float ex = cx + xx * x + yx * y; - float ey = cy + xy * x + yy * y; - CGPathAddCurveToPoint(path, nil, c1x, c1y, c2x, c2y, ex, ey); - } + float c1x = cx + xx * cp1x + yx * cp1y; + float c1y = cy + xy * cp1x + yy * cp1y; + float c2x = cx + xx * cp2x + yx * cp2y; + float c2y = cy + xy * cp2x + yy * cp2y; + float ex = cx + xx * x + yx * y; + float ey = cy + xy * x + yy * y; + CGPathAddCurveToPoint(path, nil, c1x, c1y, c2x, c2y, ex, ey); + } } - (void)close:(CGMutablePathRef)path { - if (_penDownSet) { - _penX = _penDownX; - _penY = _penDownY; - _penDownSet = NO; - CGPathCloseSubpath(path); - } + if (_penDownSet) { + _penX = _penDownX; + _penY = _penDownY; + _penDownSet = NO; + CGPathCloseSubpath(path); + } } - (void)setPenDown { - if (!_penDownSet) { - _penDownX = _penX; - _penDownY = _penY; - _penDownSet = YES; - } + if (!_penDownSet) { + _penDownX = _penX; + _penDownY = _penY; + _penDownSet = YES; + } } -- (void)skip_spaces { - while (i < l && [[NSCharacterSet whitespaceAndNewlineCharacterSet] characterIsMember:[s characterAtIndex:i]]) i++; +- (void)skip_spaces +{ + while (i < l && [[NSCharacterSet whitespaceAndNewlineCharacterSet] characterIsMember:[s characterAtIndex:i]]) + i++; } -- (bool)is_cmd:(char)c { - switch (c) { - case 'M': - case 'm': - case 'Z': - case 'z': - case 'L': - case 'l': - case 'H': - case 'h': - case 'V': - case 'v': - case 'C': - case 'c': - case 'S': - case 's': - case 'Q': - case 'q': - case 'T': - case 't': - case 'A': - case 'a': - return true; - } - return false; +- (bool)is_cmd:(char)c +{ + switch (c) { + case 'M': + case 'm': + case 'Z': + case 'z': + case 'L': + case 'l': + case 'H': + case 'h': + case 'V': + case 'v': + case 'C': + case 'c': + case 'S': + case 's': + case 'Q': + case 'q': + case 'T': + case 't': + case 'A': + case 'a': + return true; + } + return false; } -- (bool)is_number_start:(char)c { - return (c >= '0' && c <= '9') || c == '.' || c == '-' || c == '+'; +- (bool)is_number_start:(char)c +{ + return (c >= '0' && c <= '9') || c == '.' || c == '-' || c == '+'; } -- (bool)is_absolute:(char)c { - return [[NSCharacterSet uppercaseLetterCharacterSet] characterIsMember:c]; +- (bool)is_absolute:(char)c +{ + return [[NSCharacterSet uppercaseLetterCharacterSet] characterIsMember:c]; } // By the SVG spec 'large-arc' and 'sweep' must contain only one char // and can be written without any separators, e.g.: 10 20 30 01 10 20. -- (bool)parse_flag { - [self skip_spaces]; +- (bool)parse_flag +{ + [self skip_spaces]; - char c = [s characterAtIndex:i]; - switch (c) { - case '0': - case '1': { - i += 1; - if (i < l && [s characterAtIndex:i] == ',') { - i += 1; - } - [self skip_spaces]; - break; - } - default: - RCTLogError(@"UnexpectedData: %@", s); - } - - return c == '1'; -} - -- (float)parse_list_number { - if (i == l) { - RCTLogError(@"UnexpectedEnd: %@", s); - } - - float n = [self parse_number]; - [self skip_spaces]; - [self parse_list_separator]; - - return n; -} - -- (float)parse_number { - // Strip off leading whitespaces. - [self skip_spaces]; - - if (i == l) { - RCTLogError(@"InvalidNumber: %@", s); - } - - NSUInteger start = i; - - char c = [s characterAtIndex:i]; - - // Consume sign. - if (c == '-' || c == '+') { + char c = [s characterAtIndex:i]; + switch (c) { + case '0': + case '1': { + i += 1; + if (i < l && [s characterAtIndex:i] == ',') { i += 1; - c = [s characterAtIndex:i]; + } + [self skip_spaces]; + break; } + default: + RCTLogError(@"UnexpectedData: %@", s); + } - // Consume integer. - if (c >= '0' && c <= '9') { - [self skip_digits]; - if (i < l) { - c = [s characterAtIndex:i]; - } - } else if (c != '.') { - RCTLogError(@"InvalidNumber: %@", s); + return c == '1'; +} + +- (float)parse_list_number +{ + if (i == l) { + RCTLogError(@"UnexpectedEnd: %@", s); + } + + float n = [self parse_number]; + [self skip_spaces]; + [self parse_list_separator]; + + return n; +} + +- (float)parse_number +{ + // Strip off leading whitespaces. + [self skip_spaces]; + + if (i == l) { + RCTLogError(@"InvalidNumber: %@", s); + } + + NSUInteger start = i; + + char c = [s characterAtIndex:i]; + + // Consume sign. + if (c == '-' || c == '+') { + i += 1; + c = [s characterAtIndex:i]; + } + + // Consume integer. + if (c >= '0' && c <= '9') { + [self skip_digits]; + if (i < l) { + c = [s characterAtIndex:i]; } + } else if (c != '.') { + RCTLogError(@"InvalidNumber: %@", s); + } - // Consume fraction. - if (c == '.') { + // Consume fraction. + if (c == '.') { + i += 1; + [self skip_digits]; + if (i < l) { + c = [s characterAtIndex:i]; + } + } + + if ((c == 'e' || c == 'E') && i + 1 < l) { + char c2 = [s characterAtIndex:i + 1]; + // Check for `em`/`ex`. + if (c2 != 'm' && c2 != 'x') { + i += 1; + c = [s characterAtIndex:i]; + + if (c == '+' || c == '-') { i += 1; [self skip_digits]; - if (i < l) { - c = [s characterAtIndex:i]; - } - } - - if ((c == 'e' || c == 'E') && i + 1 < l) { - char c2 = [s characterAtIndex:i + 1]; - // Check for `em`/`ex`. - if (c2 != 'm' && c2 != 'x') { - i += 1; - c = [s characterAtIndex:i]; - - if (c == '+' || c == '-') { - i += 1; - [self skip_digits]; - } else if (c >= '0' && c <= '9') { - [self skip_digits]; - } else { - RCTLogError(@"InvalidNumber: %@", s); - } - } - } - - NSString* num = [s substringWithRange:NSMakeRange(start, i - start)]; - float n = [num floatValue]; - - // inf, nan, etc. are an error. - if (!isfinite(n)) { + } else if (c >= '0' && c <= '9') { + [self skip_digits]; + } else { RCTLogError(@"InvalidNumber: %@", s); + } } + } - return n; + NSString *num = [s substringWithRange:NSMakeRange(start, i - start)]; + float n = [num floatValue]; + + // inf, nan, etc. are an error. + if (!isfinite(n)) { + RCTLogError(@"InvalidNumber: %@", s); + } + + return n; } -- (void)parse_list_separator { - if (i < l && [s characterAtIndex:i] == ',') { - i += 1; - } +- (void)parse_list_separator +{ + if (i < l && [s characterAtIndex:i] == ',') { + i += 1; + } } -- (void)skip_digits { - while (i < l && [[NSCharacterSet decimalDigitCharacterSet] characterIsMember:[s characterAtIndex:i]]) i++; +- (void)skip_digits +{ + while (i < l && [[NSCharacterSet decimalDigitCharacterSet] characterIsMember:[s characterAtIndex:i]]) + i++; } @end - diff --git a/apple/Utils/RNSVGUnits.h b/apple/Utils/RNSVGUnits.h index 0592bc52..d524ea03 100644 --- a/apple/Utils/RNSVGUnits.h +++ b/apple/Utils/RNSVGUnits.h @@ -7,6 +7,6 @@ */ typedef CF_ENUM(int32_t, RNSVGUnits) { - kRNSVGUnitsObjectBoundingBox, - kRNSVGUnitsUserSpaceOnUse + kRNSVGUnitsObjectBoundingBox, + kRNSVGUnitsUserSpaceOnUse }; diff --git a/apple/Utils/RNSVGVBMOS.h b/apple/Utils/RNSVGVBMOS.h index 24c404a2..03725968 100644 --- a/apple/Utils/RNSVGVBMOS.h +++ b/apple/Utils/RNSVGVBMOS.h @@ -7,7 +7,7 @@ */ typedef CF_ENUM(int32_t, RNSVGVBMOS) { - kRNSVGVBMOSMeet, - kRNSVGVBMOSSlice, - kRNSVGVBMOSNone + kRNSVGVBMOSMeet, + kRNSVGVBMOSSlice, + kRNSVGVBMOSNone }; diff --git a/apple/Utils/RNSVGVectorEffect.h b/apple/Utils/RNSVGVectorEffect.h index 0ab17036..3dee2add 100644 --- a/apple/Utils/RNSVGVectorEffect.h +++ b/apple/Utils/RNSVGVectorEffect.h @@ -7,8 +7,8 @@ */ typedef CF_ENUM(int32_t, RNSVGVectorEffect) { - kRNSVGVectorEffectDefault, - kRNSVGVectorEffectNonScalingStroke, - kRNSVGVectorEffectInherit, - kRNSVGVectorEffectUri + kRNSVGVectorEffectDefault, + kRNSVGVectorEffectNonScalingStroke, + kRNSVGVectorEffectInherit, + kRNSVGVectorEffectUri }; diff --git a/apple/Utils/RNSVGViewBox.h b/apple/Utils/RNSVGViewBox.h index 2c7f51ae..d7defd75 100644 --- a/apple/Utils/RNSVGViewBox.h +++ b/apple/Utils/RNSVGViewBox.h @@ -12,6 +12,9 @@ @interface RNSVGViewBox : NSObject -+ (CGAffineTransform)getTransform:(CGRect)vbRect eRect:(CGRect)eRect align:(NSString *)align meetOrSlice:(RNSVGVBMOS)meetOrSlice; ++ (CGAffineTransform)getTransform:(CGRect)vbRect + eRect:(CGRect)eRect + align:(NSString *)align + meetOrSlice:(RNSVGVBMOS)meetOrSlice; @end diff --git a/apple/Utils/RNSVGViewBox.mm b/apple/Utils/RNSVGViewBox.mm index 3c01208b..d24604fa 100644 --- a/apple/Utils/RNSVGViewBox.mm +++ b/apple/Utils/RNSVGViewBox.mm @@ -6,93 +6,99 @@ * LICENSE file in the root directory of this source tree. */ -#import #import "RNSVGViewBox.h" +#import #import "RNSVGUse.h" @implementation RNSVGViewBox -+ (CGAffineTransform)getTransform:(CGRect)vbRect eRect:(CGRect)eRect align:(NSString *)align meetOrSlice:(RNSVGVBMOS)meetOrSlice ++ (CGAffineTransform)getTransform:(CGRect)vbRect + eRect:(CGRect)eRect + align:(NSString *)align + meetOrSlice:(RNSVGVBMOS)meetOrSlice { - // based on https://svgwg.org/svg2-draft/coords.html#ComputingAViewportsTransform + // based on https://svgwg.org/svg2-draft/coords.html#ComputingAViewportsTransform - // Let vb-x, vb-y, vb-width, vb-height be the min-x, min-y, width and height values of the viewBox attribute respectively. - CGFloat vbX = CGRectGetMinX(vbRect); - CGFloat vbY = CGRectGetMinY(vbRect); - CGFloat vbWidth = CGRectGetWidth(vbRect); - CGFloat vbHeight = CGRectGetHeight(vbRect); + // Let vb-x, vb-y, vb-width, vb-height be the min-x, min-y, width and height values of the viewBox attribute + // respectively. + CGFloat vbX = CGRectGetMinX(vbRect); + CGFloat vbY = CGRectGetMinY(vbRect); + CGFloat vbWidth = CGRectGetWidth(vbRect); + CGFloat vbHeight = CGRectGetHeight(vbRect); - // Let e-x, e-y, e-width, e-height be the position and size of the element respectively. - CGFloat eX = CGRectGetMinX(eRect); - CGFloat eY = CGRectGetMinY(eRect); - CGFloat eWidth = CGRectGetWidth(eRect); - CGFloat eHeight = CGRectGetHeight(eRect); + // Let e-x, e-y, e-width, e-height be the position and size of the element respectively. + CGFloat eX = CGRectGetMinX(eRect); + CGFloat eY = CGRectGetMinY(eRect); + CGFloat eWidth = CGRectGetWidth(eRect); + CGFloat eHeight = CGRectGetHeight(eRect); - // Let align be the align value of preserveAspectRatio, or 'xMidyMid' if preserveAspectRatio is not defined. + // Let align be the align value of preserveAspectRatio, or 'xMidyMid' if preserveAspectRatio is not defined. - // Let meetOrSlice be the meetOrSlice value of preserveAspectRatio, or 'meet' if preserveAspectRatio is not defined or if meetOrSlice is missing from this value. + // Let meetOrSlice be the meetOrSlice value of preserveAspectRatio, or 'meet' if preserveAspectRatio is not defined or + // if meetOrSlice is missing from this value. - // Initialize scale-x to e-width/vb-width. - CGFloat scaleX = eWidth / vbWidth; + // Initialize scale-x to e-width/vb-width. + CGFloat scaleX = eWidth / vbWidth; - // Initialize scale-y to e-height/vb-height. - CGFloat scaleY = eHeight / vbHeight; + // Initialize scale-y to e-height/vb-height. + CGFloat scaleY = eHeight / vbHeight; - // Initialize translate-x to e-x - (vb-x * scale-x). - // Initialize translate-y to e-y - (vb-y * scale-y). - CGFloat translateX = eX - (vbX * scaleX); - CGFloat translateY = eY - (vbY * scaleY); + // Initialize translate-x to e-x - (vb-x * scale-x). + // Initialize translate-y to e-y - (vb-y * scale-y). + CGFloat translateX = eX - (vbX * scaleX); + CGFloat translateY = eY - (vbY * scaleY); - // If align is 'none' - if (meetOrSlice == kRNSVGVBMOSNone) { - // Let scale be set the smaller value of scale-x and scale-y. - // Assign scale-x and scale-y to scale. - CGFloat scale = scaleX = scaleY = fmin(scaleX, scaleY); + // If align is 'none' + if (meetOrSlice == kRNSVGVBMOSNone) { + // Let scale be set the smaller value of scale-x and scale-y. + // Assign scale-x and scale-y to scale. + CGFloat scale = scaleX = scaleY = fmin(scaleX, scaleY); - // If scale is greater than 1 - if (scale > 1) { - // Minus translateX by (eWidth / scale - vbWidth) / 2 - // Minus translateY by (eHeight / scale - vbHeight) / 2 - translateX -= (eWidth / scale - vbWidth) / 2; - translateY -= (eHeight / scale - vbHeight) / 2; - } else { - translateX -= (eWidth - vbWidth * scale) / 2; - translateY -= (eHeight - vbHeight * scale) / 2; - } + // If scale is greater than 1 + if (scale > 1) { + // Minus translateX by (eWidth / scale - vbWidth) / 2 + // Minus translateY by (eHeight / scale - vbHeight) / 2 + translateX -= (eWidth / scale - vbWidth) / 2; + translateY -= (eHeight / scale - vbHeight) / 2; } else { - // If align is not 'none' and meetOrSlice is 'meet', set the larger of scale-x and scale-y to the smaller. - // Otherwise, if align is not 'none' and meetOrSlice is 'slice', set the smaller of scale-x and scale-y to the larger. - if (![align isEqualToString: @"none"] && meetOrSlice == kRNSVGVBMOSMeet) { - scaleX = scaleY = fmin(scaleX, scaleY); - } else if (![align isEqualToString: @"none"] && meetOrSlice == kRNSVGVBMOSSlice) { - scaleX = scaleY = fmax(scaleX, scaleY); - } - - // If align contains 'xMid', add (e-width - vb-width * scale-x) / 2 to translate-x. - if ([align containsString:@"xMid"]) { - translateX += (eWidth - vbWidth * scaleX) / 2.0; - } - - // If align contains 'xMax', add (e-width - vb-width * scale-x) to translate-x. - if ([align containsString:@"xMax"]) { - translateX += (eWidth - vbWidth * scaleX); - } - - // If align contains 'yMid', add (e-height - vb-height * scale-y) / 2 to translate-y. - if ([align containsString:@"YMid"]) { - translateY += (eHeight - vbHeight * scaleY) / 2.0; - } - - // If align contains 'yMax', add (e-height - vb-height * scale-y) to translate-y. - if ([align containsString:@"YMax"]) { - translateY += (eHeight - vbHeight * scaleY); - } + translateX -= (eWidth - vbWidth * scale) / 2; + translateY -= (eHeight - vbHeight * scale) / 2; + } + } else { + // If align is not 'none' and meetOrSlice is 'meet', set the larger of scale-x and scale-y to the smaller. + // Otherwise, if align is not 'none' and meetOrSlice is 'slice', set the smaller of scale-x and scale-y to the + // larger. + if (![align isEqualToString:@"none"] && meetOrSlice == kRNSVGVBMOSMeet) { + scaleX = scaleY = fmin(scaleX, scaleY); + } else if (![align isEqualToString:@"none"] && meetOrSlice == kRNSVGVBMOSSlice) { + scaleX = scaleY = fmax(scaleX, scaleY); } - // The transform applied to content contained by the element is given by - // translate(translate-x, translate-y) scale(scale-x, scale-y). - CGAffineTransform transform = CGAffineTransformMakeTranslation(translateX, translateY); - return CGAffineTransformScale(transform, scaleX, scaleY); + // If align contains 'xMid', add (e-width - vb-width * scale-x) / 2 to translate-x. + if ([align containsString:@"xMid"]) { + translateX += (eWidth - vbWidth * scaleX) / 2.0; + } + + // If align contains 'xMax', add (e-width - vb-width * scale-x) to translate-x. + if ([align containsString:@"xMax"]) { + translateX += (eWidth - vbWidth * scaleX); + } + + // If align contains 'yMid', add (e-height - vb-height * scale-y) / 2 to translate-y. + if ([align containsString:@"YMid"]) { + translateY += (eHeight - vbHeight * scaleY) / 2.0; + } + + // If align contains 'yMax', add (e-height - vb-height * scale-y) to translate-y. + if ([align containsString:@"YMax"]) { + translateY += (eHeight - vbHeight * scaleY); + } + } + + // The transform applied to content contained by the element is given by + // translate(translate-x, translate-y) scale(scale-x, scale-y). + CGAffineTransform transform = CGAffineTransformMakeTranslation(translateX, translateY); + return CGAffineTransformScale(transform, scaleX, scaleY); } @end diff --git a/apple/ViewManagers/RNSVGCircleManager.mm b/apple/ViewManagers/RNSVGCircleManager.mm index 12788ffb..4d27c410 100644 --- a/apple/ViewManagers/RNSVGCircleManager.mm +++ b/apple/ViewManagers/RNSVGCircleManager.mm @@ -8,8 +8,8 @@ #import "RNSVGCircleManager.h" -#import "RNSVGCircle.h" #import "RCTConvert+RNSVG.h" +#import "RNSVGCircle.h" @implementation RNSVGCircleManager @@ -17,11 +17,11 @@ RCT_EXPORT_MODULE() - (RNSVGRenderable *)node { - return [RNSVGCircle new]; + return [RNSVGCircle new]; } -RCT_EXPORT_VIEW_PROPERTY(cx, RNSVGLength*) -RCT_EXPORT_VIEW_PROPERTY(cy, RNSVGLength*) -RCT_EXPORT_VIEW_PROPERTY(r, RNSVGLength*) +RCT_EXPORT_VIEW_PROPERTY(cx, RNSVGLength *) +RCT_EXPORT_VIEW_PROPERTY(cy, RNSVGLength *) +RCT_EXPORT_VIEW_PROPERTY(r, RNSVGLength *) @end diff --git a/apple/ViewManagers/RNSVGDefsManager.mm b/apple/ViewManagers/RNSVGDefsManager.mm index 83a80400..38af11fd 100644 --- a/apple/ViewManagers/RNSVGDefsManager.mm +++ b/apple/ViewManagers/RNSVGDefsManager.mm @@ -20,7 +20,7 @@ RCT_EXPORT_MODULE() - (RNSVGView *)view { - return [self node]; + return [self node]; } @end diff --git a/apple/ViewManagers/RNSVGEllipseManager.mm b/apple/ViewManagers/RNSVGEllipseManager.mm index da143075..7028d7b6 100644 --- a/apple/ViewManagers/RNSVGEllipseManager.mm +++ b/apple/ViewManagers/RNSVGEllipseManager.mm @@ -8,8 +8,8 @@ #import "RNSVGEllipseManager.h" -#import "RNSVGEllipse.h" #import "RCTConvert+RNSVG.h" +#import "RNSVGEllipse.h" @implementation RNSVGEllipseManager @@ -20,9 +20,9 @@ RCT_EXPORT_MODULE() return [RNSVGEllipse new]; } -RCT_EXPORT_VIEW_PROPERTY(cx, RNSVGLength*) -RCT_EXPORT_VIEW_PROPERTY(cy, RNSVGLength*) -RCT_EXPORT_VIEW_PROPERTY(rx, RNSVGLength*) -RCT_EXPORT_VIEW_PROPERTY(ry, RNSVGLength*) +RCT_EXPORT_VIEW_PROPERTY(cx, RNSVGLength *) +RCT_EXPORT_VIEW_PROPERTY(cy, RNSVGLength *) +RCT_EXPORT_VIEW_PROPERTY(rx, RNSVGLength *) +RCT_EXPORT_VIEW_PROPERTY(ry, RNSVGLength *) @end diff --git a/apple/ViewManagers/RNSVGForeignObjectManager.mm b/apple/ViewManagers/RNSVGForeignObjectManager.mm index c7acbec1..51c585f7 100644 --- a/apple/ViewManagers/RNSVGForeignObjectManager.mm +++ b/apple/ViewManagers/RNSVGForeignObjectManager.mm @@ -15,20 +15,20 @@ RCT_EXPORT_MODULE() - (RNSVGForeignObject *)node { - return [RNSVGForeignObject new]; + return [RNSVGForeignObject new]; } -RCT_EXPORT_VIEW_PROPERTY(x, RNSVGLength*) -RCT_EXPORT_VIEW_PROPERTY(y, RNSVGLength*) -RCT_EXPORT_VIEW_PROPERTY(foreignObjectheight, RNSVGLength*) -RCT_EXPORT_VIEW_PROPERTY(foreignObjectwidth, RNSVGLength*) +RCT_EXPORT_VIEW_PROPERTY(x, RNSVGLength *) +RCT_EXPORT_VIEW_PROPERTY(y, RNSVGLength *) +RCT_EXPORT_VIEW_PROPERTY(foreignObjectheight, RNSVGLength *) +RCT_EXPORT_VIEW_PROPERTY(foreignObjectwidth, RNSVGLength *) RCT_CUSTOM_VIEW_PROPERTY(height, id, RNSVGForeignObject) { - view.foreignObjectheight = [RCTConvert RNSVGLength:json]; + view.foreignObjectheight = [RCTConvert RNSVGLength:json]; } RCT_CUSTOM_VIEW_PROPERTY(width, id, RNSVGForeignObject) { - view.foreignObjectwidth = [RCTConvert RNSVGLength:json]; + view.foreignObjectwidth = [RCTConvert RNSVGLength:json]; } @end diff --git a/apple/ViewManagers/RNSVGGroupManager.mm b/apple/ViewManagers/RNSVGGroupManager.mm index d29c0a9a..e7239220 100644 --- a/apple/ViewManagers/RNSVGGroupManager.mm +++ b/apple/ViewManagers/RNSVGGroupManager.mm @@ -23,26 +23,26 @@ RCT_EXPORT_VIEW_PROPERTY(font, NSDictionary) RCT_CUSTOM_VIEW_PROPERTY(fontSize, id, RNSVGGroup) { - if ([json isKindOfClass:[NSString class]]) { - NSString *stringValue = (NSString *)json; - view.font = @{ @"fontSize": stringValue }; - } else { - NSNumber* number = (NSNumber*)json; - double num = [number doubleValue]; - view.font = @{@"fontSize": [NSNumber numberWithDouble:num] }; - } + if ([json isKindOfClass:[NSString class]]) { + NSString *stringValue = (NSString *)json; + view.font = @{@"fontSize" : stringValue}; + } else { + NSNumber *number = (NSNumber *)json; + double num = [number doubleValue]; + view.font = @{@"fontSize" : [NSNumber numberWithDouble:num]}; + } } RCT_CUSTOM_VIEW_PROPERTY(fontWeight, id, RNSVGGroup) { - if ([json isKindOfClass:[NSString class]]) { - NSString *stringValue = (NSString *)json; - view.font = @{ @"fontWeight": stringValue }; - } else { - NSNumber* number = (NSNumber*)json; - double num = [number doubleValue]; - view.font = @{@"fontWeight": [NSNumber numberWithDouble:num] }; - } + if ([json isKindOfClass:[NSString class]]) { + NSString *stringValue = (NSString *)json; + view.font = @{@"fontWeight" : stringValue}; + } else { + NSNumber *number = (NSNumber *)json; + double num = [number doubleValue]; + view.font = @{@"fontWeight" : [NSNumber numberWithDouble:num]}; + } } @end diff --git a/apple/ViewManagers/RNSVGImageManager.mm b/apple/ViewManagers/RNSVGImageManager.mm index 96202a75..4c03bac6 100644 --- a/apple/ViewManagers/RNSVGImageManager.mm +++ b/apple/ViewManagers/RNSVGImageManager.mm @@ -7,9 +7,9 @@ */ #import "RNSVGImageManager.h" -#import "RNSVGVBMOS.h" -#import "RNSVGImage.h" #import "RCTConvert+RNSVG.h" +#import "RNSVGImage.h" +#import "RNSVGVBMOS.h" @implementation RNSVGImageManager @@ -17,23 +17,23 @@ RCT_EXPORT_MODULE() - (RNSVGRenderable *)node { - RNSVGImage *svgImage = [RNSVGImage new]; - svgImage.bridge = self.bridge; + RNSVGImage *svgImage = [RNSVGImage new]; + svgImage.bridge = self.bridge; - return svgImage; + return svgImage; } -RCT_EXPORT_VIEW_PROPERTY(x, RNSVGLength*) -RCT_EXPORT_VIEW_PROPERTY(y, RNSVGLength*) -RCT_EXPORT_VIEW_PROPERTY(imagewidth, RNSVGLength*) -RCT_EXPORT_VIEW_PROPERTY(imageheight, RNSVGLength*) +RCT_EXPORT_VIEW_PROPERTY(x, RNSVGLength *) +RCT_EXPORT_VIEW_PROPERTY(y, RNSVGLength *) +RCT_EXPORT_VIEW_PROPERTY(imagewidth, RNSVGLength *) +RCT_EXPORT_VIEW_PROPERTY(imageheight, RNSVGLength *) RCT_CUSTOM_VIEW_PROPERTY(width, id, RNSVGImage) { - view.imagewidth = [RCTConvert RNSVGLength:json]; + view.imagewidth = [RCTConvert RNSVGLength:json]; } RCT_CUSTOM_VIEW_PROPERTY(height, id, RNSVGImage) { - view.imageheight = [RCTConvert RNSVGLength:json]; + view.imageheight = [RCTConvert RNSVGLength:json]; } RCT_EXPORT_VIEW_PROPERTY(src, id) RCT_EXPORT_VIEW_PROPERTY(align, NSString) diff --git a/apple/ViewManagers/RNSVGLineManager.mm b/apple/ViewManagers/RNSVGLineManager.mm index 65954ce1..6735c617 100644 --- a/apple/ViewManagers/RNSVGLineManager.mm +++ b/apple/ViewManagers/RNSVGLineManager.mm @@ -8,8 +8,8 @@ #import "RNSVGLineManager.h" -#import "RNSVGLine.h" #import "RCTConvert+RNSVG.h" +#import "RNSVGLine.h" @implementation RNSVGLineManager @@ -20,9 +20,9 @@ RCT_EXPORT_MODULE() return [RNSVGLine new]; } -RCT_EXPORT_VIEW_PROPERTY(x1, RNSVGLength*) -RCT_EXPORT_VIEW_PROPERTY(y1, RNSVGLength*) -RCT_EXPORT_VIEW_PROPERTY(x2, RNSVGLength*) -RCT_EXPORT_VIEW_PROPERTY(y2, RNSVGLength*) +RCT_EXPORT_VIEW_PROPERTY(x1, RNSVGLength *) +RCT_EXPORT_VIEW_PROPERTY(y1, RNSVGLength *) +RCT_EXPORT_VIEW_PROPERTY(x2, RNSVGLength *) +RCT_EXPORT_VIEW_PROPERTY(y2, RNSVGLength *) @end diff --git a/apple/ViewManagers/RNSVGLinearGradientManager.mm b/apple/ViewManagers/RNSVGLinearGradientManager.mm index 65f0ae29..1ab55223 100644 --- a/apple/ViewManagers/RNSVGLinearGradientManager.mm +++ b/apple/ViewManagers/RNSVGLinearGradientManager.mm @@ -18,10 +18,10 @@ RCT_EXPORT_MODULE() return [RNSVGLinearGradient new]; } -RCT_EXPORT_VIEW_PROPERTY(x1, RNSVGLength*) -RCT_EXPORT_VIEW_PROPERTY(y1, RNSVGLength*) -RCT_EXPORT_VIEW_PROPERTY(x2, RNSVGLength*) -RCT_EXPORT_VIEW_PROPERTY(y2, RNSVGLength*) +RCT_EXPORT_VIEW_PROPERTY(x1, RNSVGLength *) +RCT_EXPORT_VIEW_PROPERTY(y1, RNSVGLength *) +RCT_EXPORT_VIEW_PROPERTY(x2, RNSVGLength *) +RCT_EXPORT_VIEW_PROPERTY(y2, RNSVGLength *) RCT_EXPORT_VIEW_PROPERTY(gradient, NSArray) RCT_EXPORT_VIEW_PROPERTY(gradientUnits, RNSVGUnits) RCT_EXPORT_VIEW_PROPERTY(gradientTransform, CGAffineTransform) diff --git a/apple/ViewManagers/RNSVGMarkerManager.mm b/apple/ViewManagers/RNSVGMarkerManager.mm index 00b76227..dd89840d 100644 --- a/apple/ViewManagers/RNSVGMarkerManager.mm +++ b/apple/ViewManagers/RNSVGMarkerManager.mm @@ -15,15 +15,15 @@ RCT_EXPORT_MODULE() - (RNSVGMarker *)node { - return [RNSVGMarker new]; + return [RNSVGMarker new]; } -RCT_EXPORT_VIEW_PROPERTY(refX, RNSVGLength*) -RCT_EXPORT_VIEW_PROPERTY(refY, RNSVGLength*) -RCT_EXPORT_VIEW_PROPERTY(markerHeight, RNSVGLength*) -RCT_EXPORT_VIEW_PROPERTY(markerWidth, RNSVGLength*) -RCT_EXPORT_VIEW_PROPERTY(markerUnits, NSString*) -RCT_EXPORT_VIEW_PROPERTY(orient, NSString*) +RCT_EXPORT_VIEW_PROPERTY(refX, RNSVGLength *) +RCT_EXPORT_VIEW_PROPERTY(refY, RNSVGLength *) +RCT_EXPORT_VIEW_PROPERTY(markerHeight, RNSVGLength *) +RCT_EXPORT_VIEW_PROPERTY(markerWidth, RNSVGLength *) +RCT_EXPORT_VIEW_PROPERTY(markerUnits, NSString *) +RCT_EXPORT_VIEW_PROPERTY(orient, NSString *) RCT_EXPORT_VIEW_PROPERTY(minX, CGFloat) RCT_EXPORT_VIEW_PROPERTY(minY, CGFloat) @@ -33,4 +33,3 @@ RCT_EXPORT_VIEW_PROPERTY(align, NSString) RCT_EXPORT_VIEW_PROPERTY(meetOrSlice, RNSVGVBMOS) @end - diff --git a/apple/ViewManagers/RNSVGMaskManager.mm b/apple/ViewManagers/RNSVGMaskManager.mm index f1a78f62..89abae1f 100644 --- a/apple/ViewManagers/RNSVGMaskManager.mm +++ b/apple/ViewManagers/RNSVGMaskManager.mm @@ -15,20 +15,20 @@ RCT_EXPORT_MODULE() - (RNSVGMask *)node { - return [RNSVGMask new]; + return [RNSVGMask new]; } -RCT_EXPORT_VIEW_PROPERTY(x, RNSVGLength*) -RCT_EXPORT_VIEW_PROPERTY(y, RNSVGLength*) -RCT_EXPORT_VIEW_PROPERTY(maskheight, RNSVGLength*) -RCT_EXPORT_VIEW_PROPERTY(maskwidth, RNSVGLength*) +RCT_EXPORT_VIEW_PROPERTY(x, RNSVGLength *) +RCT_EXPORT_VIEW_PROPERTY(y, RNSVGLength *) +RCT_EXPORT_VIEW_PROPERTY(maskheight, RNSVGLength *) +RCT_EXPORT_VIEW_PROPERTY(maskwidth, RNSVGLength *) RCT_CUSTOM_VIEW_PROPERTY(height, id, RNSVGMask) { - view.maskheight = [RCTConvert RNSVGLength:json]; + view.maskheight = [RCTConvert RNSVGLength:json]; } RCT_CUSTOM_VIEW_PROPERTY(width, id, RNSVGMask) { - view.maskwidth = [RCTConvert RNSVGLength:json]; + view.maskwidth = [RCTConvert RNSVGLength:json]; } RCT_EXPORT_VIEW_PROPERTY(maskUnits, RNSVGUnits) RCT_EXPORT_VIEW_PROPERTY(maskContentUnits, RNSVGUnits) diff --git a/apple/ViewManagers/RNSVGNodeManager.h b/apple/ViewManagers/RNSVGNodeManager.h index 2075bf61..42be52c4 100644 --- a/apple/ViewManagers/RNSVGNodeManager.h +++ b/apple/ViewManagers/RNSVGNodeManager.h @@ -6,8 +6,8 @@ * LICENSE file in the root directory of this source tree. */ -#import "RNSVGNode.h" #import +#import "RNSVGNode.h" @interface RNSVGNodeManager : RCTViewManager diff --git a/apple/ViewManagers/RNSVGNodeManager.mm b/apple/ViewManagers/RNSVGNodeManager.mm index 418bf479..c77937ff 100644 --- a/apple/ViewManagers/RNSVGNodeManager.mm +++ b/apple/ViewManagers/RNSVGNodeManager.mm @@ -16,139 +16,140 @@ static const NSUInteger kMatrixArrayLength = 4 * 4; + (CGFloat)convertToRadians:(id)json { - if ([json isKindOfClass:[NSString class]]) { - NSString *stringValue = (NSString *)json; - if ([stringValue hasSuffix:@"deg"]) { - CGFloat degrees = [[stringValue substringToIndex:stringValue.length - 3] floatValue]; - return degrees * (CGFloat)M_PI / 180; - } - if ([stringValue hasSuffix:@"rad"]) { - return [[stringValue substringToIndex:stringValue.length - 3] floatValue]; - } + if ([json isKindOfClass:[NSString class]]) { + NSString *stringValue = (NSString *)json; + if ([stringValue hasSuffix:@"deg"]) { + CGFloat degrees = [[stringValue substringToIndex:stringValue.length - 3] floatValue]; + return degrees * (CGFloat)M_PI / 180; } - return [json floatValue]; + if ([stringValue hasSuffix:@"rad"]) { + return [[stringValue substringToIndex:stringValue.length - 3] floatValue]; + } + } + return [json floatValue]; } + (CATransform3D)CATransform3DFromMatrix:(id)json { - CATransform3D transform = CATransform3DIdentity; - if (!json) { - return transform; - } - if (![json isKindOfClass:[NSArray class]]) { - RCTLogConvertError(json, @"a CATransform3D. Expected array for transform matrix."); - return transform; - } - NSArray *array = json; - if ([array count] != kMatrixArrayLength) { - RCTLogConvertError(json, @"a CATransform3D. Expected 4x4 matrix array."); - return transform; - } - for (NSUInteger i = 0; i < kMatrixArrayLength; i++) { - ((CGFloat *)&transform)[i] = [RCTConvert CGFloat:array[i]]; - } + CATransform3D transform = CATransform3DIdentity; + if (!json) { return transform; + } + if (![json isKindOfClass:[NSArray class]]) { + RCTLogConvertError(json, @"a CATransform3D. Expected array for transform matrix."); + return transform; + } + NSArray *array = json; + if ([array count] != kMatrixArrayLength) { + RCTLogConvertError(json, @"a CATransform3D. Expected 4x4 matrix array."); + return transform; + } + for (NSUInteger i = 0; i < kMatrixArrayLength; i++) { + ((CGFloat *)&transform)[i] = [RCTConvert CGFloat:array[i]]; + } + return transform; } + (CATransform3D)CATransform3D:(id)json { - CATransform3D transform = CATransform3DIdentity; - if (!json) { - return transform; - } - if (![json isKindOfClass:[NSArray class]]) { - RCTLogConvertError(json, @"a CATransform3D. Did you pass something other than an array?"); - return transform; - } - // legacy matrix support - if ([(NSArray *)json count] == kMatrixArrayLength && [json[0] isKindOfClass:[NSNumber class]]) { - RCTLogWarn(@"[RCTConvert CATransform3D:] has deprecated a matrix as input. Pass an array of configs (which can contain a matrix key) instead."); - return [self CATransform3DFromMatrix:json]; - } - - CGFloat zeroScaleThreshold = FLT_EPSILON; - - for (NSDictionary *transformConfig in (NSArray *)json) { - if (transformConfig.count != 1) { - RCTLogConvertError(json, @"a CATransform3D. You must specify exactly one property per transform object."); - return transform; - } - NSString *property = transformConfig.allKeys[0]; - id value = transformConfig[property]; - - if ([property isEqualToString:@"matrix"]) { - transform = [self CATransform3DFromMatrix:value]; - - } else if ([property isEqualToString:@"perspective"]) { - transform.m34 = -1 / [value floatValue]; - - } else if ([property isEqualToString:@"rotateX"]) { - CGFloat rotate = [self convertToRadians:value]; - transform = CATransform3DRotate(transform, rotate, 1, 0, 0); - - } else if ([property isEqualToString:@"rotateY"]) { - CGFloat rotate = [self convertToRadians:value]; - transform = CATransform3DRotate(transform, rotate, 0, 1, 0); - - } else if ([property isEqualToString:@"rotate"] || [property isEqualToString:@"rotateZ"]) { - CGFloat rotate = [self convertToRadians:value]; - transform = CATransform3DRotate(transform, rotate, 0, 0, 1); - - } else if ([property isEqualToString:@"scale"]) { - CGFloat scale = [value floatValue]; - scale = ABS(scale) < zeroScaleThreshold ? zeroScaleThreshold : scale; - transform = CATransform3DScale(transform, scale, scale, 1); - - } else if ([property isEqualToString:@"scaleX"]) { - CGFloat scale = [value floatValue]; - scale = ABS(scale) < zeroScaleThreshold ? zeroScaleThreshold : scale; - transform = CATransform3DScale(transform, scale, 1, 1); - - } else if ([property isEqualToString:@"scaleY"]) { - CGFloat scale = [value floatValue]; - scale = ABS(scale) < zeroScaleThreshold ? zeroScaleThreshold : scale; - transform = CATransform3DScale(transform, 1, scale, 1); - - } else if ([property isEqualToString:@"translate"]) { - NSArray *array = (NSArray *)value; - CGFloat translateX = [array[0] floatValue]; - CGFloat translateY = [array[1] floatValue]; - CGFloat translateZ = array.count > 2 ? [array[2] floatValue] : 0; - transform = CATransform3DTranslate(transform, translateX, translateY, translateZ); - - } else if ([property isEqualToString:@"translateX"]) { - CGFloat translate = [value floatValue]; - transform = CATransform3DTranslate(transform, translate, 0, 0); - - } else if ([property isEqualToString:@"translateY"]) { - CGFloat translate = [value floatValue]; - transform = CATransform3DTranslate(transform, 0, translate, 0); - - } else if ([property isEqualToString:@"skewX"]) { - CGFloat skew = [self convertToRadians:value]; - transform.m21 = tanf((float)skew); - - } else if ([property isEqualToString:@"skewY"]) { - CGFloat skew = [self convertToRadians:value]; - transform.m12 = tanf((float)skew); - - } else { - RCTLogError(@"Unsupported transform type for a CATransform3D: %@.", property); - } - } + CATransform3D transform = CATransform3DIdentity; + if (!json) { return transform; + } + if (![json isKindOfClass:[NSArray class]]) { + RCTLogConvertError(json, @"a CATransform3D. Did you pass something other than an array?"); + return transform; + } + // legacy matrix support + if ([(NSArray *)json count] == kMatrixArrayLength && [json[0] isKindOfClass:[NSNumber class]]) { + RCTLogWarn( + @"[RCTConvert CATransform3D:] has deprecated a matrix as input. Pass an array of configs (which can contain a matrix key) instead."); + return [self CATransform3DFromMatrix:json]; + } + + CGFloat zeroScaleThreshold = FLT_EPSILON; + + for (NSDictionary *transformConfig in (NSArray *)json) { + if (transformConfig.count != 1) { + RCTLogConvertError(json, @"a CATransform3D. You must specify exactly one property per transform object."); + return transform; + } + NSString *property = transformConfig.allKeys[0]; + id value = transformConfig[property]; + + if ([property isEqualToString:@"matrix"]) { + transform = [self CATransform3DFromMatrix:value]; + + } else if ([property isEqualToString:@"perspective"]) { + transform.m34 = -1 / [value floatValue]; + + } else if ([property isEqualToString:@"rotateX"]) { + CGFloat rotate = [self convertToRadians:value]; + transform = CATransform3DRotate(transform, rotate, 1, 0, 0); + + } else if ([property isEqualToString:@"rotateY"]) { + CGFloat rotate = [self convertToRadians:value]; + transform = CATransform3DRotate(transform, rotate, 0, 1, 0); + + } else if ([property isEqualToString:@"rotate"] || [property isEqualToString:@"rotateZ"]) { + CGFloat rotate = [self convertToRadians:value]; + transform = CATransform3DRotate(transform, rotate, 0, 0, 1); + + } else if ([property isEqualToString:@"scale"]) { + CGFloat scale = [value floatValue]; + scale = ABS(scale) < zeroScaleThreshold ? zeroScaleThreshold : scale; + transform = CATransform3DScale(transform, scale, scale, 1); + + } else if ([property isEqualToString:@"scaleX"]) { + CGFloat scale = [value floatValue]; + scale = ABS(scale) < zeroScaleThreshold ? zeroScaleThreshold : scale; + transform = CATransform3DScale(transform, scale, 1, 1); + + } else if ([property isEqualToString:@"scaleY"]) { + CGFloat scale = [value floatValue]; + scale = ABS(scale) < zeroScaleThreshold ? zeroScaleThreshold : scale; + transform = CATransform3DScale(transform, 1, scale, 1); + + } else if ([property isEqualToString:@"translate"]) { + NSArray *array = (NSArray *)value; + CGFloat translateX = [array[0] floatValue]; + CGFloat translateY = [array[1] floatValue]; + CGFloat translateZ = array.count > 2 ? [array[2] floatValue] : 0; + transform = CATransform3DTranslate(transform, translateX, translateY, translateZ); + + } else if ([property isEqualToString:@"translateX"]) { + CGFloat translate = [value floatValue]; + transform = CATransform3DTranslate(transform, translate, 0, 0); + + } else if ([property isEqualToString:@"translateY"]) { + CGFloat translate = [value floatValue]; + transform = CATransform3DTranslate(transform, 0, translate, 0); + + } else if ([property isEqualToString:@"skewX"]) { + CGFloat skew = [self convertToRadians:value]; + transform.m21 = tanf((float)skew); + + } else if ([property isEqualToString:@"skewY"]) { + CGFloat skew = [self convertToRadians:value]; + transform.m12 = tanf((float)skew); + + } else { + RCTLogError(@"Unsupported transform type for a CATransform3D: %@.", property); + } + } + return transform; } RCT_EXPORT_MODULE() - (RNSVGNode *)node { - return [RNSVGNode new]; + return [RNSVGNode new]; } - (RNSVGView *)view { - return [self node]; + return [self node]; } RCT_EXPORT_VIEW_PROPERTY(name, NSString) @@ -156,11 +157,11 @@ RCT_EXPORT_VIEW_PROPERTY(opacity, CGFloat) RCT_EXPORT_VIEW_PROPERTY(matrix, CGAffineTransform) RCT_CUSTOM_VIEW_PROPERTY(transform, CATransform3D, RNSVGNode) { - CATransform3D transform3d = json ? [RNSVGNodeManager CATransform3D:json] : defaultView.layer.transform; - CGAffineTransform transform = CATransform3DGetAffineTransform(transform3d); - view.invTransform = CGAffineTransformInvert(transform); - view.transforms = transform; - [view invalidate]; + CATransform3D transform3d = json ? [RNSVGNodeManager CATransform3D:json] : defaultView.layer.transform; + CGAffineTransform transform = CATransform3DGetAffineTransform(transform3d); + view.invTransform = CGAffineTransformInvert(transform); + view.transforms = transform; + [view invalidate]; } RCT_EXPORT_VIEW_PROPERTY(mask, NSString) RCT_EXPORT_VIEW_PROPERTY(markerStart, NSString) @@ -231,14 +232,14 @@ RCT_CUSTOM_SHADOW_PROPERTY(overflow, id, RNSVGNode) {} RCT_CUSTOM_SHADOW_PROPERTY(display, id, RNSVGNode) {} RCT_CUSTOM_VIEW_PROPERTY(display, id, RNSVGNode) { - view.display = json; + view.display = json; } RCT_CUSTOM_SHADOW_PROPERTY(direction, id, RNSVGNode) {} RCT_CUSTOM_VIEW_PROPERTY(pointerEvents, RCTPointerEvents, RNSVGNode) { - view.pointerEvents = json ? [RCTConvert RCTPointerEvents:json] : defaultView.pointerEvents; + view.pointerEvents = json ? [RCTConvert RCTPointerEvents:json] : defaultView.pointerEvents; } @end diff --git a/apple/ViewManagers/RNSVGPathManager.mm b/apple/ViewManagers/RNSVGPathManager.mm index ae4648dc..8282413f 100644 --- a/apple/ViewManagers/RNSVGPathManager.mm +++ b/apple/ViewManagers/RNSVGPathManager.mm @@ -8,8 +8,8 @@ #import "RNSVGPathManager.h" -#import "RNSVGPath.h" #import "RCTConvert+RNSVG.h" +#import "RNSVGPath.h" @implementation RNSVGPathManager diff --git a/apple/ViewManagers/RNSVGPatternManager.mm b/apple/ViewManagers/RNSVGPatternManager.mm index 9715525b..134a5a7c 100644 --- a/apple/ViewManagers/RNSVGPatternManager.mm +++ b/apple/ViewManagers/RNSVGPatternManager.mm @@ -15,21 +15,21 @@ RCT_EXPORT_MODULE() - (RNSVGPattern *)node { - return [RNSVGPattern new]; + return [RNSVGPattern new]; } -RCT_EXPORT_VIEW_PROPERTY(x, RNSVGLength*) -RCT_EXPORT_VIEW_PROPERTY(y, RNSVGLength*) -RCT_EXPORT_VIEW_PROPERTY(patternheight, RNSVGLength*) -RCT_EXPORT_VIEW_PROPERTY(patternwidth, RNSVGLength*) +RCT_EXPORT_VIEW_PROPERTY(x, RNSVGLength *) +RCT_EXPORT_VIEW_PROPERTY(y, RNSVGLength *) +RCT_EXPORT_VIEW_PROPERTY(patternheight, RNSVGLength *) +RCT_EXPORT_VIEW_PROPERTY(patternwidth, RNSVGLength *) RCT_CUSTOM_VIEW_PROPERTY(height, id, RNSVGPattern) { - view.patternheight = [RCTConvert RNSVGLength:json]; + view.patternheight = [RCTConvert RNSVGLength:json]; } RCT_CUSTOM_VIEW_PROPERTY(width, id, RNSVGPattern) { - view.patternwidth = [RCTConvert RNSVGLength:json]; + view.patternwidth = [RCTConvert RNSVGLength:json]; } RCT_EXPORT_VIEW_PROPERTY(patternUnits, RNSVGUnits) RCT_EXPORT_VIEW_PROPERTY(patternContentUnits, RNSVGUnits) diff --git a/apple/ViewManagers/RNSVGRadialGradientManager.mm b/apple/ViewManagers/RNSVGRadialGradientManager.mm index 61d0d3c4..da4ef26d 100644 --- a/apple/ViewManagers/RNSVGRadialGradientManager.mm +++ b/apple/ViewManagers/RNSVGRadialGradientManager.mm @@ -18,12 +18,12 @@ RCT_EXPORT_MODULE() return [RNSVGRadialGradient new]; } -RCT_EXPORT_VIEW_PROPERTY(fx, RNSVGLength*) -RCT_EXPORT_VIEW_PROPERTY(fy, RNSVGLength*) -RCT_EXPORT_VIEW_PROPERTY(cx, RNSVGLength*) -RCT_EXPORT_VIEW_PROPERTY(cy, RNSVGLength*) -RCT_EXPORT_VIEW_PROPERTY(rx, RNSVGLength*) -RCT_EXPORT_VIEW_PROPERTY(ry, RNSVGLength*) +RCT_EXPORT_VIEW_PROPERTY(fx, RNSVGLength *) +RCT_EXPORT_VIEW_PROPERTY(fy, RNSVGLength *) +RCT_EXPORT_VIEW_PROPERTY(cx, RNSVGLength *) +RCT_EXPORT_VIEW_PROPERTY(cy, RNSVGLength *) +RCT_EXPORT_VIEW_PROPERTY(rx, RNSVGLength *) +RCT_EXPORT_VIEW_PROPERTY(ry, RNSVGLength *) RCT_EXPORT_VIEW_PROPERTY(gradient, NSArray) RCT_EXPORT_VIEW_PROPERTY(gradientUnits, RNSVGUnits) RCT_EXPORT_VIEW_PROPERTY(gradientTransform, CGAffineTransform) diff --git a/apple/ViewManagers/RNSVGRectManager.mm b/apple/ViewManagers/RNSVGRectManager.mm index 8c2b8eca..05e516e3 100644 --- a/apple/ViewManagers/RNSVGRectManager.mm +++ b/apple/ViewManagers/RNSVGRectManager.mm @@ -8,8 +8,8 @@ #import "RNSVGRectManager.h" -#import "RNSVGRect.h" #import "RCTConvert+RNSVG.h" +#import "RNSVGRect.h" @implementation RNSVGRectManager @@ -20,20 +20,20 @@ RCT_EXPORT_MODULE() return [RNSVGRect new]; } -RCT_EXPORT_VIEW_PROPERTY(x, RNSVGLength*) -RCT_EXPORT_VIEW_PROPERTY(y, RNSVGLength*) -RCT_EXPORT_VIEW_PROPERTY(rectheight, RNSVGLength*) -RCT_EXPORT_VIEW_PROPERTY(rectwidth, RNSVGLength*) +RCT_EXPORT_VIEW_PROPERTY(x, RNSVGLength *) +RCT_EXPORT_VIEW_PROPERTY(y, RNSVGLength *) +RCT_EXPORT_VIEW_PROPERTY(rectheight, RNSVGLength *) +RCT_EXPORT_VIEW_PROPERTY(rectwidth, RNSVGLength *) RCT_CUSTOM_VIEW_PROPERTY(height, id, RNSVGRect) { - view.rectheight = [RCTConvert RNSVGLength:json]; + view.rectheight = [RCTConvert RNSVGLength:json]; } RCT_CUSTOM_VIEW_PROPERTY(width, id, RNSVGRect) { - view.rectwidth = [RCTConvert RNSVGLength:json]; + view.rectwidth = [RCTConvert RNSVGLength:json]; } -RCT_EXPORT_VIEW_PROPERTY(rx, RNSVGLength*) -RCT_EXPORT_VIEW_PROPERTY(ry, RNSVGLength*) +RCT_EXPORT_VIEW_PROPERTY(rx, RNSVGLength *) +RCT_EXPORT_VIEW_PROPERTY(ry, RNSVGLength *) @end diff --git a/apple/ViewManagers/RNSVGRenderableManager.mm b/apple/ViewManagers/RNSVGRenderableManager.mm index 9a11f526..3352e23d 100644 --- a/apple/ViewManagers/RNSVGRenderableManager.mm +++ b/apple/ViewManagers/RNSVGRenderableManager.mm @@ -6,10 +6,10 @@ * LICENSE file in the root directory of this source tree. */ +#import "RNSVGRenderableManager.h" #import #import #import -#import "RNSVGRenderableManager.h" #import "RNSVGPathMeasure.h" #import "RCTConvert+RNSVG.h" @@ -21,7 +21,7 @@ RCT_EXPORT_MODULE() - (RNSVGRenderable *)node { - return [RNSVGRenderable new]; + return [RNSVGRenderable new]; } RCT_EXPORT_VIEW_PROPERTY(fill, RNSVGBrush) @@ -29,7 +29,7 @@ RCT_EXPORT_VIEW_PROPERTY(fillOpacity, CGFloat) RCT_EXPORT_VIEW_PROPERTY(fillRule, RNSVGCGFCRule) RCT_EXPORT_VIEW_PROPERTY(stroke, RNSVGBrush) RCT_EXPORT_VIEW_PROPERTY(strokeOpacity, CGFloat) -RCT_EXPORT_VIEW_PROPERTY(strokeWidth, RNSVGLength*) +RCT_EXPORT_VIEW_PROPERTY(strokeWidth, RNSVGLength *) RCT_EXPORT_VIEW_PROPERTY(strokeLinecap, CGLineCap) RCT_EXPORT_VIEW_PROPERTY(strokeLinejoin, CGLineJoin) RCT_EXPORT_VIEW_PROPERTY(strokeDasharray, NSArray) @@ -38,199 +38,177 @@ RCT_EXPORT_VIEW_PROPERTY(strokeMiterlimit, CGFloat) RCT_EXPORT_VIEW_PROPERTY(vectorEffect, int) RCT_EXPORT_VIEW_PROPERTY(propList, NSArray) -RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(isPointInFill:(nonnull NSNumber *)reactTag options:(NSDictionary *)options) +RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(isPointInFill : (nonnull NSNumber *)reactTag options : (NSDictionary *)options) { - RNSVGPlatformView *view = [self getRenderableView:reactTag]; + RNSVGPlatformView *view = [self getRenderableView:reactTag]; - if (![view isKindOfClass:[RNSVGRenderable class]]) { - RCTLogError(@"Invalid svg returned from registry, expecting RNSVGRenderable, got: %@", view); - return [NSNumber numberWithBool:false]; - } - if (options == nil) { - RCTLogError(@"Invalid options given to isPointInFill, got: %@", options); - return [NSNumber numberWithBool:false]; - } - id xo = [options objectForKey:@"x"]; - id yo = [options objectForKey:@"y"]; - if (![xo isKindOfClass:NSNumber.class] || - ![yo isKindOfClass:NSNumber.class]) { - RCTLogError(@"Invalid x or y given to isPointInFill"); - return [NSNumber numberWithBool:false]; - } - RNSVGRenderable *svg = (RNSVGRenderable *)view; - CGFloat x = (CGFloat)[xo doubleValue]; - CGFloat y = (CGFloat)[yo doubleValue]; - CGPoint point = CGPointMake(x, y); - RNSVGPlatformView *target = [svg hitTest:point withEvent:nil]; - BOOL hit = target != nil; - return [NSNumber numberWithBool:hit]; + if (![view isKindOfClass:[RNSVGRenderable class]]) { + RCTLogError(@"Invalid svg returned from registry, expecting RNSVGRenderable, got: %@", view); + return [NSNumber numberWithBool:false]; + } + if (options == nil) { + RCTLogError(@"Invalid options given to isPointInFill, got: %@", options); + return [NSNumber numberWithBool:false]; + } + id xo = [options objectForKey:@"x"]; + id yo = [options objectForKey:@"y"]; + if (![xo isKindOfClass:NSNumber.class] || ![yo isKindOfClass:NSNumber.class]) { + RCTLogError(@"Invalid x or y given to isPointInFill"); + return [NSNumber numberWithBool:false]; + } + RNSVGRenderable *svg = (RNSVGRenderable *)view; + CGFloat x = (CGFloat)[xo doubleValue]; + CGFloat y = (CGFloat)[yo doubleValue]; + CGPoint point = CGPointMake(x, y); + RNSVGPlatformView *target = [svg hitTest:point withEvent:nil]; + BOOL hit = target != nil; + return [NSNumber numberWithBool:hit]; } -RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(isPointInStroke:(nonnull NSNumber *)reactTag options:(NSDictionary *)options) +RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(isPointInStroke : (nonnull NSNumber *)reactTag options : (NSDictionary *)options) { - RNSVGPlatformView *view = [self getRenderableView:reactTag]; + RNSVGPlatformView *view = [self getRenderableView:reactTag]; - if (![view isKindOfClass:[RNSVGRenderable class]]) { - RCTLogError(@"Invalid svg returned from registry, expecting RNSVGRenderable, got: %@", view); - return [NSNumber numberWithBool:false]; - } - if (options == nil) { - RCTLogError(@"Invalid options given to isPointInFill, got: %@", options); - return [NSNumber numberWithBool:false]; - } - id xo = [options objectForKey:@"x"]; - id yo = [options objectForKey:@"y"]; - if (![xo isKindOfClass:NSNumber.class] || - ![yo isKindOfClass:NSNumber.class]) { - RCTLogError(@"Invalid x or y given to isPointInFill"); - return [NSNumber numberWithBool:false]; - } - RNSVGRenderable *svg = (RNSVGRenderable *)view; - CGFloat x = (CGFloat)[xo doubleValue]; - CGFloat y = (CGFloat)[yo doubleValue]; - CGPoint point = CGPointMake(x, y); - BOOL hit = CGPathContainsPoint(svg.strokePath, nil, point, NO); + if (![view isKindOfClass:[RNSVGRenderable class]]) { + RCTLogError(@"Invalid svg returned from registry, expecting RNSVGRenderable, got: %@", view); + return [NSNumber numberWithBool:false]; + } + if (options == nil) { + RCTLogError(@"Invalid options given to isPointInFill, got: %@", options); + return [NSNumber numberWithBool:false]; + } + id xo = [options objectForKey:@"x"]; + id yo = [options objectForKey:@"y"]; + if (![xo isKindOfClass:NSNumber.class] || ![yo isKindOfClass:NSNumber.class]) { + RCTLogError(@"Invalid x or y given to isPointInFill"); + return [NSNumber numberWithBool:false]; + } + RNSVGRenderable *svg = (RNSVGRenderable *)view; + CGFloat x = (CGFloat)[xo doubleValue]; + CGFloat y = (CGFloat)[yo doubleValue]; + CGPoint point = CGPointMake(x, y); + BOOL hit = CGPathContainsPoint(svg.strokePath, nil, point, NO); - return [NSNumber numberWithBool:hit]; + return [NSNumber numberWithBool:hit]; } -RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getTotalLength:(nonnull NSNumber *)reactTag) +RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getTotalLength : (nonnull NSNumber *)reactTag) { - RNSVGPlatformView *view = [self getRenderableView:reactTag]; + RNSVGPlatformView *view = [self getRenderableView:reactTag]; - if (![view isKindOfClass:[RNSVGRenderable class]]) { - RCTLogError(@"Invalid svg returned from registry, expecting RNSVGRenderable, got: %@", view); - return [NSNumber numberWithDouble:0]; - } + if (![view isKindOfClass:[RNSVGRenderable class]]) { + RCTLogError(@"Invalid svg returned from registry, expecting RNSVGRenderable, got: %@", view); + return [NSNumber numberWithDouble:0]; + } - RNSVGPathMeasure *measure = [[RNSVGPathMeasure alloc]init]; - RNSVGRenderable *svg = (RNSVGRenderable *)view; - CGPathRef target = [svg getPath:nil]; - [measure extractPathData:target]; + RNSVGPathMeasure *measure = [[RNSVGPathMeasure alloc] init]; + RNSVGRenderable *svg = (RNSVGRenderable *)view; + CGPathRef target = [svg getPath:nil]; + [measure extractPathData:target]; - return [NSNumber numberWithDouble:measure.pathLength]; + return [NSNumber numberWithDouble:measure.pathLength]; } -RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getPointAtLength:(nonnull NSNumber *)reactTag options:(NSDictionary *)options) +RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getPointAtLength + : (nonnull NSNumber *)reactTag options + : (NSDictionary *)options) { - RNSVGPlatformView *view = [self getRenderableView:reactTag]; + RNSVGPlatformView *view = [self getRenderableView:reactTag]; - if (![view isKindOfClass:[RNSVGRenderable class]]) { - RCTLogError(@"Invalid svg returned from registry, expecting RNSVGRenderable, got: %@", view); - return nil; - } + if (![view isKindOfClass:[RNSVGRenderable class]]) { + RCTLogError(@"Invalid svg returned from registry, expecting RNSVGRenderable, got: %@", view); + return nil; + } - CGFloat position = (CGFloat)[[options objectForKey:@"length"] doubleValue]; - RNSVGPathMeasure *measure = [[RNSVGPathMeasure alloc]init]; - RNSVGRenderable *svg = (RNSVGRenderable *)view; - CGPathRef target = [svg getPath:nil]; - [measure extractPathData:target]; + CGFloat position = (CGFloat)[[options objectForKey:@"length"] doubleValue]; + RNSVGPathMeasure *measure = [[RNSVGPathMeasure alloc] init]; + RNSVGRenderable *svg = (RNSVGRenderable *)view; + CGPathRef target = [svg getPath:nil]; + [measure extractPathData:target]; - CGFloat x; - CGFloat y; - CGFloat angle; - double midPoint = fmax(0, fmin(position, measure.pathLength)); - [measure getPosAndTan:&angle midPoint:midPoint x:&x y:&y]; + CGFloat x; + CGFloat y; + CGFloat angle; + double midPoint = fmax(0, fmin(position, measure.pathLength)); + [measure getPosAndTan:&angle midPoint:midPoint x:&x y:&y]; - return @{ - @"x":@(x), - @"y":@(y), - @"angle":@(angle) - }; + return @{@"x" : @(x), @"y" : @(y), @"angle" : @(angle)}; } -RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getBBox:(nonnull NSNumber *)reactTag options:(NSDictionary *)options) +RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getBBox : (nonnull NSNumber *)reactTag options : (NSDictionary *)options) { - RNSVGPlatformView *view = [self getRenderableView:reactTag]; + RNSVGPlatformView *view = [self getRenderableView:reactTag]; - if (![view isKindOfClass:[RNSVGRenderable class]]) { - RCTLogError(@"Invalid svg returned from registry, expecting RNSVGRenderable, got: %@", view); - return nil; - } + if (![view isKindOfClass:[RNSVGRenderable class]]) { + RCTLogError(@"Invalid svg returned from registry, expecting RNSVGRenderable, got: %@", view); + return nil; + } - RNSVGRenderable *svg = (RNSVGRenderable *)view; - BOOL fill = [[options objectForKey:@"fill"] boolValue]; - BOOL stroke = [[options objectForKey:@"stroke"] boolValue]; - BOOL markers = [[options objectForKey:@"markers"] boolValue]; - BOOL clipped = [[options objectForKey:@"clipped"] boolValue]; - [svg getPath:nil]; + RNSVGRenderable *svg = (RNSVGRenderable *)view; + BOOL fill = [[options objectForKey:@"fill"] boolValue]; + BOOL stroke = [[options objectForKey:@"stroke"] boolValue]; + BOOL markers = [[options objectForKey:@"markers"] boolValue]; + BOOL clipped = [[options objectForKey:@"clipped"] boolValue]; + [svg getPath:nil]; - CGRect bounds = CGRectZero; - if (fill) { - bounds = CGRectUnion(bounds, svg.fillBounds); - } - if (stroke) { - bounds = CGRectUnion(bounds, svg.strokeBounds); - } - if (markers) { - bounds = CGRectUnion(bounds, svg.markerBounds); - } - if (clipped) { - CGPathRef clipPath = [svg getClipPath]; - CGRect clipBounds = CGPathGetBoundingBox(clipPath); - if (clipPath && !CGRectIsEmpty(clipBounds)) { - bounds = CGRectIntersection(bounds, clipBounds); - } + CGRect bounds = CGRectZero; + if (fill) { + bounds = CGRectUnion(bounds, svg.fillBounds); + } + if (stroke) { + bounds = CGRectUnion(bounds, svg.strokeBounds); + } + if (markers) { + bounds = CGRectUnion(bounds, svg.markerBounds); + } + if (clipped) { + CGPathRef clipPath = [svg getClipPath]; + CGRect clipBounds = CGPathGetBoundingBox(clipPath); + if (clipPath && !CGRectIsEmpty(clipBounds)) { + bounds = CGRectIntersection(bounds, clipBounds); } + } - CGPoint origin = bounds.origin; - CGSize size = bounds.size; - return @{ - @"x":@(origin.x), - @"y":@(origin.y), - @"width":@(size.width), - @"height":@(size.height) - }; + CGPoint origin = bounds.origin; + CGSize size = bounds.size; + return @{@"x" : @(origin.x), @"y" : @(origin.y), @"width" : @(size.width), @"height" : @(size.height)}; } -RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getCTM:(nonnull NSNumber *)reactTag) +RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getCTM : (nonnull NSNumber *)reactTag) { - RNSVGPlatformView *view = [self getRenderableView:reactTag]; + RNSVGPlatformView *view = [self getRenderableView:reactTag]; - if (![view isKindOfClass:[RNSVGRenderable class]]) { - RCTLogError(@"Invalid svg returned from registry, expecting RNSVGRenderable, got: %@", view); - return nil; - } + if (![view isKindOfClass:[RNSVGRenderable class]]) { + RCTLogError(@"Invalid svg returned from registry, expecting RNSVGRenderable, got: %@", view); + return nil; + } - RNSVGRenderable *svg = (RNSVGRenderable *)view; - CGAffineTransform ctm = svg.ctm; - return @{ - @"a":@(ctm.a), - @"b":@(ctm.b), - @"c":@(ctm.c), - @"d":@(ctm.d), - @"e":@(ctm.tx), - @"f":@(ctm.ty) - }; + RNSVGRenderable *svg = (RNSVGRenderable *)view; + CGAffineTransform ctm = svg.ctm; + return @{@"a" : @(ctm.a), @"b" : @(ctm.b), @"c" : @(ctm.c), @"d" : @(ctm.d), @"e" : @(ctm.tx), @"f" : @(ctm.ty)}; } -RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getScreenCTM:(nonnull NSNumber *)reactTag) +RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getScreenCTM : (nonnull NSNumber *)reactTag) { - RNSVGPlatformView *view = [self getRenderableView:reactTag]; + RNSVGPlatformView *view = [self getRenderableView:reactTag]; - if (![view isKindOfClass:[RNSVGRenderable class]]) { - RCTLogError(@"Invalid svg returned from registry, expecting RNSVGRenderable, got: %@", view); - return nil; - } + if (![view isKindOfClass:[RNSVGRenderable class]]) { + RCTLogError(@"Invalid svg returned from registry, expecting RNSVGRenderable, got: %@", view); + return nil; + } - RNSVGRenderable *svg = (RNSVGRenderable *)view; - CGAffineTransform ctm = svg.ctm; - return @{ - @"a":@(ctm.a), - @"b":@(ctm.b), - @"c":@(ctm.c), - @"d":@(ctm.d), - @"e":@(ctm.tx), - @"f":@(ctm.ty) - }; + RNSVGRenderable *svg = (RNSVGRenderable *)view; + CGAffineTransform ctm = svg.ctm; + return @{@"a" : @(ctm.a), @"b" : @(ctm.b), @"c" : @(ctm.c), @"d" : @(ctm.d), @"e" : @(ctm.tx), @"f" : @(ctm.ty)}; } -- (RNSVGPlatformView *)getRenderableView:(NSNumber *)reactTag { - __block RNSVGPlatformView *view; - dispatch_sync(dispatch_get_main_queue(), ^{ - view = [self.bridge.uiManager viewForReactTag:reactTag]; - }); - return view; +- (RNSVGPlatformView *)getRenderableView:(NSNumber *)reactTag +{ + __block RNSVGPlatformView *view; + dispatch_sync(dispatch_get_main_queue(), ^{ + view = [self.bridge.uiManager viewForReactTag:reactTag]; + }); + return view; } @end diff --git a/apple/ViewManagers/RNSVGSvgViewManager.mm b/apple/ViewManagers/RNSVGSvgViewManager.mm index 0dda5001..f69f1e14 100644 --- a/apple/ViewManagers/RNSVGSvgViewManager.mm +++ b/apple/ViewManagers/RNSVGSvgViewManager.mm @@ -6,10 +6,10 @@ * LICENSE file in the root directory of this source tree. */ +#import "RNSVGSvgViewManager.h" #import #import #import -#import "RNSVGSvgViewManager.h" #import "RNSVGSvgView.h" @implementation RNSVGSvgViewManager @@ -18,11 +18,11 @@ RCT_EXPORT_MODULE() - (RNSVGView *)view { - return [RNSVGSvgView new]; + return [RNSVGSvgView new]; } -RCT_EXPORT_VIEW_PROPERTY(bbWidth, RNSVGLength*) -RCT_EXPORT_VIEW_PROPERTY(bbHeight, RNSVGLength*) +RCT_EXPORT_VIEW_PROPERTY(bbWidth, RNSVGLength *) +RCT_EXPORT_VIEW_PROPERTY(bbHeight, RNSVGLength *) RCT_EXPORT_VIEW_PROPERTY(minX, CGFloat) RCT_EXPORT_VIEW_PROPERTY(minY, CGFloat) RCT_EXPORT_VIEW_PROPERTY(vbWidth, CGFloat) @@ -31,59 +31,65 @@ RCT_EXPORT_VIEW_PROPERTY(align, NSString) RCT_EXPORT_VIEW_PROPERTY(meetOrSlice, RNSVGVBMOS) RCT_CUSTOM_VIEW_PROPERTY(tintColor, id, RNSVGSvgView) { - view.tintColor = [RCTConvert UIColor:json]; + view.tintColor = [RCTConvert UIColor:json]; } RCT_CUSTOM_VIEW_PROPERTY(color, id, RNSVGSvgView) { - view.tintColor = [RCTConvert UIColor:json]; + view.tintColor = [RCTConvert UIColor:json]; } - -- (void)toDataURL:(nonnull NSNumber *)reactTag options:(NSDictionary *)options callback:(RCTResponseSenderBlock)callback attempt:(int)attempt { - [self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary *viewRegistry) { +- (void)toDataURL:(nonnull NSNumber *)reactTag + options:(NSDictionary *)options + callback:(RCTResponseSenderBlock)callback + attempt:(int)attempt +{ + [self.bridge.uiManager + addUIBlock:^(RCTUIManager *uiManager, NSDictionary *viewRegistry) { __kindof RNSVGPlatformView *view = [uiManager viewForReactTag:reactTag]; - NSString * b64; + NSString *b64; if ([view isKindOfClass:[RNSVGSvgView class]]) { - RNSVGSvgView *svg = view; - if (options == nil) { - b64 = [svg getDataURL]; - } else { - id width = [options objectForKey:@"width"]; - id height = [options objectForKey:@"height"]; - if (![width isKindOfClass:NSNumber.class] || - ![height isKindOfClass:NSNumber.class]) { - RCTLogError(@"Invalid width or height given to toDataURL"); - return; - } - NSNumber* w = width; - NSInteger wi = (NSInteger)[w intValue]; - NSNumber* h = height; - NSInteger hi = (NSInteger)[h intValue]; - - CGRect bounds = CGRectMake(0, 0, wi, hi); - b64 = [svg getDataURLwithBounds:bounds]; + RNSVGSvgView *svg = view; + if (options == nil) { + b64 = [svg getDataURL]; + } else { + id width = [options objectForKey:@"width"]; + id height = [options objectForKey:@"height"]; + if (![width isKindOfClass:NSNumber.class] || ![height isKindOfClass:NSNumber.class]) { + RCTLogError(@"Invalid width or height given to toDataURL"); + return; } + NSNumber *w = width; + NSInteger wi = (NSInteger)[w intValue]; + NSNumber *h = height; + NSInteger hi = (NSInteger)[h intValue]; + + CGRect bounds = CGRectMake(0, 0, wi, hi); + b64 = [svg getDataURLwithBounds:bounds]; + } } else { - RCTLogError(@"Invalid svg returned from registry, expecting RNSVGSvgView, got: %@", view); - return; + RCTLogError(@"Invalid svg returned from registry, expecting RNSVGSvgView, got: %@", view); + return; } if (b64) { - callback(@[b64]); + callback(@[ b64 ]); } else if (attempt < 1) { - void (^retryBlock)(void) = ^{ - [self toDataURL:reactTag options:options callback:callback attempt:(attempt + 1)]; - }; + void (^retryBlock)(void) = ^{ + [self toDataURL:reactTag options:options callback:callback attempt:(attempt + 1)]; + }; - RCTExecuteOnUIManagerQueue(retryBlock); + RCTExecuteOnUIManagerQueue(retryBlock); } else { - callback(@[]); + callback(@[]); } - }]; + }]; } -RCT_EXPORT_METHOD(toDataURL:(nonnull NSNumber *)reactTag options:(NSDictionary *)options callback:(RCTResponseSenderBlock)callback) +RCT_EXPORT_METHOD(toDataURL + : (nonnull NSNumber *)reactTag options + : (NSDictionary *)options callback + : (RCTResponseSenderBlock)callback) { - [self toDataURL:reactTag options:options callback:callback attempt:0]; + [self toDataURL:reactTag options:options callback:callback attempt:0]; } @end diff --git a/apple/ViewManagers/RNSVGSymbolManager.mm b/apple/ViewManagers/RNSVGSymbolManager.mm index 81353a66..6221299a 100644 --- a/apple/ViewManagers/RNSVGSymbolManager.mm +++ b/apple/ViewManagers/RNSVGSymbolManager.mm @@ -7,9 +7,9 @@ */ #import "RNSVGSymbolManager.h" +#import "RCTConvert+RNSVG.h" #import "RNSVGRenderable.h" #import "RNSVGSymbol.h" -#import "RCTConvert+RNSVG.h" #import "RNSVGVBMOS.h" @implementation RNSVGSymbolManager diff --git a/apple/ViewManagers/RNSVGTSpanManager.mm b/apple/ViewManagers/RNSVGTSpanManager.mm index fc5ca79d..cdef5dbe 100644 --- a/apple/ViewManagers/RNSVGTSpanManager.mm +++ b/apple/ViewManagers/RNSVGTSpanManager.mm @@ -8,8 +8,8 @@ #import "RNSVGTSpanManager.h" -#import "RNSVGTSpan.h" #import "RCTConvert+RNSVG.h" +#import "RNSVGTSpan.h" @implementation RNSVGTSpanManager diff --git a/apple/ViewManagers/RNSVGTextManager.mm b/apple/ViewManagers/RNSVGTextManager.mm index 28d5eb30..628533f8 100644 --- a/apple/ViewManagers/RNSVGTextManager.mm +++ b/apple/ViewManagers/RNSVGTextManager.mm @@ -8,8 +8,8 @@ #import "RNSVGTextManager.h" -#import "RNSVGText.h" #import "RCTConvert+RNSVG.h" +#import "RNSVGText.h" @implementation RNSVGTextManager @@ -23,77 +23,77 @@ RCT_EXPORT_MODULE() RCT_EXPORT_VIEW_PROPERTY(textAnchor, RNSVGTextAnchor) RCT_CUSTOM_VIEW_PROPERTY(dx, id, RNSVGText) { - view.deltaX = [RCTConvert RNSVGLengthArray:json]; + view.deltaX = [RCTConvert RNSVGLengthArray:json]; } RCT_CUSTOM_VIEW_PROPERTY(dy, id, RNSVGText) { - view.deltaY = [RCTConvert RNSVGLengthArray:json]; + view.deltaY = [RCTConvert RNSVGLengthArray:json]; } RCT_CUSTOM_VIEW_PROPERTY(positionX, id, RNSVGText) { - view.positionX = [RCTConvert RNSVGLengthArray:json]; + view.positionX = [RCTConvert RNSVGLengthArray:json]; } RCT_CUSTOM_VIEW_PROPERTY(positionY, id, RNSVGText) { - view.positionY = [RCTConvert RNSVGLengthArray:json]; + view.positionY = [RCTConvert RNSVGLengthArray:json]; } RCT_CUSTOM_VIEW_PROPERTY(x, id, RNSVGText) { - view.positionX = [RCTConvert RNSVGLengthArray:json]; + view.positionX = [RCTConvert RNSVGLengthArray:json]; } RCT_CUSTOM_VIEW_PROPERTY(y, id, RNSVGText) { - view.positionY = [RCTConvert RNSVGLengthArray:json]; + view.positionY = [RCTConvert RNSVGLengthArray:json]; } RCT_CUSTOM_VIEW_PROPERTY(rotate, id, RNSVGText) { - view.rotate = [RCTConvert RNSVGLengthArray:json]; + view.rotate = [RCTConvert RNSVGLengthArray:json]; } RCT_EXPORT_VIEW_PROPERTY(font, NSDictionary) RCT_CUSTOM_VIEW_PROPERTY(inlineSize, id, RNSVGText) { - view.inlineSize = [RCTConvert RNSVGLength:json]; + view.inlineSize = [RCTConvert RNSVGLength:json]; } RCT_CUSTOM_VIEW_PROPERTY(textLength, id, RNSVGText) { - view.textLength = [RCTConvert RNSVGLength:json]; + view.textLength = [RCTConvert RNSVGLength:json]; } RCT_CUSTOM_VIEW_PROPERTY(baselineShift, id, RNSVGText) { - if ([json isKindOfClass:[NSString class]]) { - NSString *stringValue = (NSString *)json; - view.baselineShift = stringValue; - } else { - view.baselineShift = [NSString stringWithFormat:@"%f", [json doubleValue]]; - } + if ([json isKindOfClass:[NSString class]]) { + NSString *stringValue = (NSString *)json; + view.baselineShift = stringValue; + } else { + view.baselineShift = [NSString stringWithFormat:@"%f", [json doubleValue]]; + } } RCT_EXPORT_VIEW_PROPERTY(lengthAdjust, NSString) RCT_EXPORT_VIEW_PROPERTY(alignmentBaseline, NSString) RCT_CUSTOM_VIEW_PROPERTY(fontSize, id, RNSVGText) { - if ([json isKindOfClass:[NSString class]]) { - NSString *stringValue = (NSString *)json; - view.font = @{ @"fontSize": stringValue }; - } else { - NSNumber* number = (NSNumber*)json; - double num = [number doubleValue]; - view.font = @{@"fontSize": [NSNumber numberWithDouble:num] }; - } + if ([json isKindOfClass:[NSString class]]) { + NSString *stringValue = (NSString *)json; + view.font = @{@"fontSize" : stringValue}; + } else { + NSNumber *number = (NSNumber *)json; + double num = [number doubleValue]; + view.font = @{@"fontSize" : [NSNumber numberWithDouble:num]}; + } } RCT_CUSTOM_VIEW_PROPERTY(fontWeight, id, RNSVGText) { - if ([json isKindOfClass:[NSString class]]) { - NSString *stringValue = (NSString *)json; - view.font = @{ @"fontWeight": stringValue }; - } else { - NSNumber* number = (NSNumber*)json; - double num = [number doubleValue]; - view.font = @{@"fontWeight": [NSNumber numberWithDouble:num] }; - } + if ([json isKindOfClass:[NSString class]]) { + NSString *stringValue = (NSString *)json; + view.font = @{@"fontWeight" : stringValue}; + } else { + NSNumber *number = (NSNumber *)json; + double num = [number doubleValue]; + view.font = @{@"fontWeight" : [NSNumber numberWithDouble:num]}; + } } @end diff --git a/apple/ViewManagers/RNSVGTextPathManager.mm b/apple/ViewManagers/RNSVGTextPathManager.mm index 98d88fc3..e9c7b1a0 100644 --- a/apple/ViewManagers/RNSVGTextPathManager.mm +++ b/apple/ViewManagers/RNSVGTextPathManager.mm @@ -24,6 +24,6 @@ RCT_EXPORT_VIEW_PROPERTY(side, NSString) RCT_EXPORT_VIEW_PROPERTY(method, NSString) RCT_EXPORT_VIEW_PROPERTY(midLine, NSString) RCT_EXPORT_VIEW_PROPERTY(spacing, NSString) -RCT_EXPORT_VIEW_PROPERTY(startOffset, RNSVGLength*) +RCT_EXPORT_VIEW_PROPERTY(startOffset, RNSVGLength *) @end diff --git a/apple/ViewManagers/RNSVGUseManager.mm b/apple/ViewManagers/RNSVGUseManager.mm index 23ddaa0c..0fd695ad 100644 --- a/apple/ViewManagers/RNSVGUseManager.mm +++ b/apple/ViewManagers/RNSVGUseManager.mm @@ -21,21 +21,21 @@ RCT_EXPORT_MODULE() RCT_EXPORT_VIEW_PROPERTY(href, NSString) RCT_CUSTOM_VIEW_PROPERTY(x, id, RNSVGUse) { - view.x = [RCTConvert RNSVGLength:json]; + view.x = [RCTConvert RNSVGLength:json]; } RCT_CUSTOM_VIEW_PROPERTY(y, id, RNSVGUse) { - view.y = [RCTConvert RNSVGLength:json]; + view.y = [RCTConvert RNSVGLength:json]; } -RCT_EXPORT_VIEW_PROPERTY(useheight, RNSVGLength*) -RCT_EXPORT_VIEW_PROPERTY(usewidth, RNSVGLength*) +RCT_EXPORT_VIEW_PROPERTY(useheight, RNSVGLength *) +RCT_EXPORT_VIEW_PROPERTY(usewidth, RNSVGLength *) RCT_CUSTOM_VIEW_PROPERTY(height, id, RNSVGUse) { - view.useheight = [RCTConvert RNSVGLength:json]; + view.useheight = [RCTConvert RNSVGLength:json]; } RCT_CUSTOM_VIEW_PROPERTY(width, id, RNSVGUse) { - view.usewidth = [RCTConvert RNSVGLength:json]; + view.usewidth = [RCTConvert RNSVGLength:json]; } @end diff --git a/package.json b/package.json index 27dbde72..e2ead177 100644 --- a/package.json +++ b/package.json @@ -47,13 +47,16 @@ "bob": "bob build", "flow": "flow src", "flowtyped": "flow-typed install", - "format": "prettier --write README.md CONTRIBUTING.md CODE_OF_CONDUCT.md './src/**/*.{ts,tsx}'", + "format": "yarn format-js && yarn format-ios && yarn format-java", + "format-ios": "find apple/ -iname *.h -o -iname *.m -o -iname *.cpp -o -iname *.mm | xargs clang-format -i", + "format-java": "node ./scripts/format-java.js", + "format-js": "prettier --write README.md CONTRIBUTING.md CODE_OF_CONDUCT.md USAGE.md './src/**/*.{ts,tsx}' './Example/**/*.{ts,tsx}'", "jest": "jest", "lint": "eslint --ext .ts,.tsx src", "peg": "pegjs -o src/lib/extract/transform.js ./src/lib/extract/transform.peg", - "prepare": "npm run bob", + "prepare": "npm run bob && husky install", "release": "npm login && release-it", - "test": "npm run lint && npm run tsc && npm run flow && npm run jest", + "test": "npm run lint && npm run tsc && npm run jest", "tsc": "tsc --noEmit" }, "peerDependencies": { @@ -78,6 +81,7 @@ "@types/react-native": "^0.69.3", "babel-eslint": "^10.1.0", "babel-jest": "^28.1.0", + "clang-format": "^1.8.0", "eslint": "^8.16.0", "eslint-plugin-flowtype": "^8.0.3", "eslint-plugin-prettier": "^4.0.0", @@ -85,7 +89,9 @@ "flow-bin": "^0.119.1", "flow-typed": "^3.0.0", "flowgen": "^1.10.0", + "husky": "^8.0.1", "jest": "^28.1.0", + "lint-staged": "^13.0.3", "pegjs": "^0.10.0", "prettier": "^2.6.2", "react": "^17.0.1", @@ -96,6 +102,12 @@ "ts-node": "^10.8.0", "typescript": "^4.7.2" }, + "lint-staged": { + "{src,Example}/**/*.{js,ts,tsx}": "yarn format-js", + "src/**/*.{js,ts,tsx}": "yarn lint", + "apple/**/*.{h,m,mm,cpp}": "yarn format-ios", + "android/src/**/*.java": "yarn format-java" + }, "nativePackage": true, "codegenConfig": { "libraries": [ diff --git a/scripts/format-java.js b/scripts/format-java.js new file mode 100644 index 00000000..5fb49d0f --- /dev/null +++ b/scripts/format-java.js @@ -0,0 +1,37 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ +/* + * This script is a wrapper for gradle & spotlessApply to make + * it work properly with lint-staged. + */ +const { exit } = require('process'); +const { execSync } = require('child_process'); + +const writeToConsoleOnError = (error, stdout) => { + if (error) { + console.log(error); + console.log(stdout); + return exit(1); + } +}; + +// spotless formatting task in android/build.gradle +const spotlessApply = './android/gradlew -p android spotlessApply'; + +// takes file as parameter passed by lint-staged (optional) +const fileName = process.argv[2]; + +// https://github.com/diffplug/spotless/blob/main/plugin-gradle/IDE_HOOK.md +// creates file argument without space between arguments +const fileArgument = `-PspotlessIdeHook=${fileName}`; + +const command = + fileName !== undefined ? `${spotlessApply} ${fileArgument}` : spotlessApply; + +// reformat code +execSync(command, writeToConsoleOnError); + +// file passed by lint-staged is now reformatted +if (fileName !== undefined) { + // so stage this file again after formatting + execSync(`git add ${fileName}`, writeToConsoleOnError); +} \ No newline at end of file diff --git a/src/fabric/CircleNativeComponent.js b/src/fabric/CircleNativeComponent.js index d93feabf..4d8e59b5 100644 --- a/src/fabric/CircleNativeComponent.js +++ b/src/fabric/CircleNativeComponent.js @@ -11,17 +11,17 @@ import type { Int32, Double } from 'react-native/Libraries/Types/CodegenTypes'; type LayoutEvent = $ReadOnly<{| layout: $ReadOnly<{| - x: Float, - y: Float, - width: Float, - height: Float, + x: Float, + y: Float, + width: Float, + height: Float, |}>, |}>; type SvgNodeCommonProps = $ReadOnly<{| name?: string, opacity?: WithDefault, - matrix?: $ReadOnlyArray,//CGAffineTransform, + matrix?: $ReadOnlyArray, //CGAffineTransform, // transform?: ____TransformStyle_Internal, // CATransform3D, custom handling mask?: string, markerStart?: string, @@ -46,7 +46,7 @@ type SvgRenderableCommonProps = $ReadOnly<{| fillRule?: WithDefault, stroke?: ColorStruct, strokeOpacity?: WithDefault, - strokeWidth?: WithDefault, + strokeWidth?: WithDefault, strokeLinecap?: WithDefault, strokeLinejoin?: WithDefault, strokeDasharray?: $ReadOnlyArray, @@ -56,16 +56,18 @@ type SvgRenderableCommonProps = $ReadOnly<{| propList?: $ReadOnlyArray, |}>; - type NativeProps = $ReadOnly<{| - ...ViewProps, - ...SvgNodeCommonProps, - ...SvgRenderableCommonProps, - cx?: string, - cy?: string, - r?: string, + ...ViewProps, + ...SvgNodeCommonProps, + ...SvgRenderableCommonProps, + cx?: string, + cy?: string, + r?: string, |}>; type ComponentType = HostComponent; -export default (codegenNativeComponent('RNSVGCircle', {}): ComponentType); \ No newline at end of file +export default (codegenNativeComponent( + 'RNSVGCircle', + {}, +): ComponentType); diff --git a/src/fabric/ClipPathNativeComponent.js b/src/fabric/ClipPathNativeComponent.js index 5b16a46c..94707885 100644 --- a/src/fabric/ClipPathNativeComponent.js +++ b/src/fabric/ClipPathNativeComponent.js @@ -11,17 +11,17 @@ import type { Int32, Double } from 'react-native/Libraries/Types/CodegenTypes'; type LayoutEvent = $ReadOnly<{| layout: $ReadOnly<{| - x: Float, - y: Float, - width: Float, - height: Float, + x: Float, + y: Float, + width: Float, + height: Float, |}>, |}>; type SvgNodeCommonProps = $ReadOnly<{| name?: string, opacity?: WithDefault, - matrix?: $ReadOnlyArray,//CGAffineTransform, + matrix?: $ReadOnlyArray, //CGAffineTransform, // transform?: ____TransformStyle_Internal, // CATransform3D, custom handling mask?: string, markerStart?: string, @@ -35,10 +35,13 @@ type SvgNodeCommonProps = $ReadOnly<{| |}>; type NativeProps = $ReadOnly<{| - ...ViewProps, - ...SvgNodeCommonProps, + ...ViewProps, + ...SvgNodeCommonProps, |}>; type ComponentType = HostComponent; -export default (codegenNativeComponent('RNSVGClipPath', {}): ComponentType); \ No newline at end of file +export default (codegenNativeComponent( + 'RNSVGClipPath', + {}, +): ComponentType); diff --git a/src/fabric/DefsNativeComponent.js b/src/fabric/DefsNativeComponent.js index 05834484..cc8ac862 100644 --- a/src/fabric/DefsNativeComponent.js +++ b/src/fabric/DefsNativeComponent.js @@ -8,9 +8,12 @@ import type { ViewProps } from 'react-native/Libraries/Components/View/ViewPropT import type { HostComponent } from 'react-native/Libraries/Renderer/shims/ReactNativeTypes'; type NativeProps = $ReadOnly<{| - ...ViewProps, + ...ViewProps, |}>; type ComponentType = HostComponent; -export default (codegenNativeComponent('RNSVGDefs', {}): ComponentType); \ No newline at end of file +export default (codegenNativeComponent( + 'RNSVGDefs', + {}, +): ComponentType); diff --git a/src/fabric/EllipseNativeComponent.js b/src/fabric/EllipseNativeComponent.js index 02aaf906..40c47afc 100644 --- a/src/fabric/EllipseNativeComponent.js +++ b/src/fabric/EllipseNativeComponent.js @@ -11,17 +11,17 @@ import type { Int32, Double } from 'react-native/Libraries/Types/CodegenTypes'; type LayoutEvent = $ReadOnly<{| layout: $ReadOnly<{| - x: Float, - y: Float, - width: Float, - height: Float, + x: Float, + y: Float, + width: Float, + height: Float, |}>, |}>; type SvgNodeCommonProps = $ReadOnly<{| name?: string, opacity?: WithDefault, - matrix?: $ReadOnlyArray,//CGAffineTransform, + matrix?: $ReadOnlyArray, //CGAffineTransform, // transform?: ____TransformStyle_Internal, // CATransform3D, custom handling mask?: string, markerStart?: string, @@ -46,7 +46,7 @@ type SvgRenderableCommonProps = $ReadOnly<{| fillRule?: WithDefault, stroke?: ColorStruct, strokeOpacity?: WithDefault, - strokeWidth?: WithDefault, + strokeWidth?: WithDefault, strokeLinecap?: WithDefault, strokeLinejoin?: WithDefault, strokeDasharray?: $ReadOnlyArray, @@ -56,17 +56,19 @@ type SvgRenderableCommonProps = $ReadOnly<{| propList?: $ReadOnlyArray, |}>; - type NativeProps = $ReadOnly<{| - ...ViewProps, - ...SvgNodeCommonProps, - ...SvgRenderableCommonProps, - cx?: string, - cy?: string, - rx?: string, - ry?: string, + ...ViewProps, + ...SvgNodeCommonProps, + ...SvgRenderableCommonProps, + cx?: string, + cy?: string, + rx?: string, + ry?: string, |}>; type ComponentType = HostComponent; -export default (codegenNativeComponent('RNSVGEllipse', {}): ComponentType); \ No newline at end of file +export default (codegenNativeComponent( + 'RNSVGEllipse', + {}, +): ComponentType); diff --git a/src/fabric/ForeignObjectNativeComponent.js b/src/fabric/ForeignObjectNativeComponent.js index 2a28d92d..ed4e23cd 100644 --- a/src/fabric/ForeignObjectNativeComponent.js +++ b/src/fabric/ForeignObjectNativeComponent.js @@ -7,26 +7,24 @@ import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNati import type { HostComponent } from 'react-native/Libraries/Renderer/shims/ReactNativeTypes'; import type { ViewProps } from 'react-native/Libraries/Components/View/ViewPropTypes'; -import type { - DirectEventHandler, -} from 'react-native/Libraries/Types/CodegenTypes'; +import type { DirectEventHandler } from 'react-native/Libraries/Types/CodegenTypes'; // for some reason the parser doesn't allow to put flow types in another file // import type { SvgNodeCommonProps } from './svgNodeCommonProps'; type LayoutEvent = $ReadOnly<{| layout: $ReadOnly<{| - x: Float, - y: Float, - width: Float, - height: Float, + x: Float, + y: Float, + width: Float, + height: Float, |}>, |}>; type SvgNodeCommonProps = $ReadOnly<{| name?: string, opacity?: WithDefault, - matrix?: $ReadOnlyArray,//CGAffineTransform, + matrix?: $ReadOnlyArray, //CGAffineTransform, // transform?: ____TransformStyle_Internal, // CATransform3D, custom handling mask?: string, markerStart?: string, @@ -51,7 +49,7 @@ type SvgRenderableCommonProps = $ReadOnly<{| fillRule?: WithDefault, stroke?: ColorStruct, strokeOpacity?: WithDefault, - strokeWidth?: WithDefault, + strokeWidth?: WithDefault, strokeLinecap?: WithDefault, strokeLinejoin?: WithDefault, strokeDasharray?: $ReadOnlyArray, @@ -62,20 +60,20 @@ type SvgRenderableCommonProps = $ReadOnly<{| |}>; type FontObject = $ReadOnly<{| - fontStyle?: string; - fontVariant?: string; - fontWeight?: string; - fontStretch?: string; - fontSize?: string; - fontFamily?: string; - textAnchor?: string; - textDecoration?: string; - letterSpacing?: string; - wordSpacing?: string; - kerning?: string; - fontFeatureSettings?: string; - fontVariantLigatures?: string; - fontVariationSettings?: string; + fontStyle?: string, + fontVariant?: string, + fontWeight?: string, + fontStretch?: string, + fontSize?: string, + fontFamily?: string, + textAnchor?: string, + textDecoration?: string, + letterSpacing?: string, + wordSpacing?: string, + kerning?: string, + fontFeatureSettings?: string, + fontVariantLigatures?: string, + fontVariationSettings?: string, |}>; type SvgGroupCommonProps = $ReadOnly<{| @@ -99,4 +97,7 @@ type NativeProps = $ReadOnly<{| type ComponentType = HostComponent; -export default (codegenNativeComponent('RNSVGForeignObject', {}): ComponentType); \ No newline at end of file +export default (codegenNativeComponent( + 'RNSVGForeignObject', + {}, +): ComponentType); diff --git a/src/fabric/GroupNativeComponent.js b/src/fabric/GroupNativeComponent.js index 40005841..77bd1c01 100644 --- a/src/fabric/GroupNativeComponent.js +++ b/src/fabric/GroupNativeComponent.js @@ -7,26 +7,24 @@ import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNati import type { HostComponent } from 'react-native/Libraries/Renderer/shims/ReactNativeTypes'; import type { ViewProps } from 'react-native/Libraries/Components/View/ViewPropTypes'; -import type { - DirectEventHandler, -} from 'react-native/Libraries/Types/CodegenTypes'; +import type { DirectEventHandler } from 'react-native/Libraries/Types/CodegenTypes'; // for some reason the parser doesn't allow to put flow types in another file // import type { SvgNodeCommonProps } from './svgNodeCommonProps'; type LayoutEvent = $ReadOnly<{| layout: $ReadOnly<{| - x: Float, - y: Float, - width: Float, - height: Float, + x: Float, + y: Float, + width: Float, + height: Float, |}>, |}>; type SvgNodeCommonProps = $ReadOnly<{| name?: string, opacity?: WithDefault, - matrix?: $ReadOnlyArray,//CGAffineTransform, + matrix?: $ReadOnlyArray, //CGAffineTransform, // transform?: ____TransformStyle_Internal, // CATransform3D, custom handling mask?: string, markerStart?: string, @@ -51,7 +49,7 @@ type SvgRenderableCommonProps = $ReadOnly<{| fillRule?: WithDefault, stroke?: ColorStruct, strokeOpacity?: WithDefault, - strokeWidth?: WithDefault, + strokeWidth?: WithDefault, strokeLinecap?: WithDefault, strokeLinejoin?: WithDefault, strokeDasharray?: $ReadOnlyArray, @@ -62,20 +60,20 @@ type SvgRenderableCommonProps = $ReadOnly<{| |}>; type FontObject = $ReadOnly<{| - fontStyle?: string; - fontVariant?: string; - fontWeight?: string; - fontStretch?: string; - fontSize?: string; - fontFamily?: string; - textAnchor?: string; - textDecoration?: string; - letterSpacing?: string; - wordSpacing?: string; - kerning?: string; - fontFeatureSettings?: string; - fontVariantLigatures?: string; - fontVariationSettings?: string; + fontStyle?: string, + fontVariant?: string, + fontWeight?: string, + fontStretch?: string, + fontSize?: string, + fontFamily?: string, + textAnchor?: string, + textDecoration?: string, + letterSpacing?: string, + wordSpacing?: string, + kerning?: string, + fontFeatureSettings?: string, + fontVariantLigatures?: string, + fontVariationSettings?: string, |}>; type SvgGroupCommonProps = $ReadOnly<{| @@ -93,4 +91,7 @@ type NativeProps = $ReadOnly<{| type ComponentType = HostComponent; -export default (codegenNativeComponent('RNSVGGroup', {}): ComponentType); \ No newline at end of file +export default (codegenNativeComponent( + 'RNSVGGroup', + {}, +): ComponentType); diff --git a/src/fabric/ImageNativeComponent.js b/src/fabric/ImageNativeComponent.js index a48f4a20..50ef1879 100644 --- a/src/fabric/ImageNativeComponent.js +++ b/src/fabric/ImageNativeComponent.js @@ -11,17 +11,17 @@ import type { Int32, Double } from 'react-native/Libraries/Types/CodegenTypes'; type LayoutEvent = $ReadOnly<{| layout: $ReadOnly<{| - x: Float, - y: Float, - width: Float, - height: Float, + x: Float, + y: Float, + width: Float, + height: Float, |}>, |}>; type SvgNodeCommonProps = $ReadOnly<{| name?: string, opacity?: WithDefault, - matrix?: $ReadOnlyArray,//CGAffineTransform, + matrix?: $ReadOnlyArray, //CGAffineTransform, // transform?: ____TransformStyle_Internal, // CATransform3D, custom handling mask?: string, markerStart?: string, @@ -46,7 +46,7 @@ type SvgRenderableCommonProps = $ReadOnly<{| fillRule?: WithDefault, stroke?: ColorStruct, strokeOpacity?: WithDefault, - strokeWidth?: WithDefault, + strokeWidth?: WithDefault, strokeLinecap?: WithDefault, strokeLinejoin?: WithDefault, strokeDasharray?: $ReadOnlyArray, @@ -56,22 +56,24 @@ type SvgRenderableCommonProps = $ReadOnly<{| propList?: $ReadOnlyArray, |}>; - type NativeProps = $ReadOnly<{| - ...ViewProps, - ...SvgNodeCommonProps, - ...SvgRenderableCommonProps, - x?: string; - y?: string; - imagewidth?: string; - imageheight?: string; - width?: string; - height?: string; - src?: ImageSource; - align?: string; - meetOrSlice?: Int32; + ...ViewProps, + ...SvgNodeCommonProps, + ...SvgRenderableCommonProps, + x?: string, + y?: string, + imagewidth?: string, + imageheight?: string, + width?: string, + height?: string, + src?: ImageSource, + align?: string, + meetOrSlice?: Int32, |}>; type ComponentType = HostComponent; -export default (codegenNativeComponent('RNSVGImage', {}): ComponentType); \ No newline at end of file +export default (codegenNativeComponent( + 'RNSVGImage', + {}, +): ComponentType); diff --git a/src/fabric/LineNativeComponent.js b/src/fabric/LineNativeComponent.js index 678129c6..14ea6ab4 100644 --- a/src/fabric/LineNativeComponent.js +++ b/src/fabric/LineNativeComponent.js @@ -11,17 +11,17 @@ import type { Int32, Double } from 'react-native/Libraries/Types/CodegenTypes'; type LayoutEvent = $ReadOnly<{| layout: $ReadOnly<{| - x: Float, - y: Float, - width: Float, - height: Float, + x: Float, + y: Float, + width: Float, + height: Float, |}>, |}>; type SvgNodeCommonProps = $ReadOnly<{| name?: string, opacity?: WithDefault, - matrix?: $ReadOnlyArray,//CGAffineTransform, + matrix?: $ReadOnlyArray, //CGAffineTransform, // transform?: ____TransformStyle_Internal, // CATransform3D, custom handling mask?: string, markerStart?: string, @@ -46,7 +46,7 @@ type SvgRenderableCommonProps = $ReadOnly<{| fillRule?: WithDefault, stroke?: ColorStruct, strokeOpacity?: WithDefault, - strokeWidth?: WithDefault, + strokeWidth?: WithDefault, strokeLinecap?: WithDefault, strokeLinejoin?: WithDefault, strokeDasharray?: $ReadOnlyArray, @@ -56,17 +56,19 @@ type SvgRenderableCommonProps = $ReadOnly<{| propList?: $ReadOnlyArray, |}>; - type NativeProps = $ReadOnly<{| - ...ViewProps, - ...SvgNodeCommonProps, - ...SvgRenderableCommonProps, - x1?: string, - y1?: string, - x2?: string, - y2?: string, + ...ViewProps, + ...SvgNodeCommonProps, + ...SvgRenderableCommonProps, + x1?: string, + y1?: string, + x2?: string, + y2?: string, |}>; type ComponentType = HostComponent; -export default (codegenNativeComponent('RNSVGLine', {}): ComponentType); \ No newline at end of file +export default (codegenNativeComponent( + 'RNSVGLine', + {}, +): ComponentType); diff --git a/src/fabric/LinearGradientNativeComponent.js b/src/fabric/LinearGradientNativeComponent.js index 67e47d03..162c988d 100644 --- a/src/fabric/LinearGradientNativeComponent.js +++ b/src/fabric/LinearGradientNativeComponent.js @@ -11,17 +11,17 @@ import type { Int32, Double } from 'react-native/Libraries/Types/CodegenTypes'; type LayoutEvent = $ReadOnly<{| layout: $ReadOnly<{| - x: Float, - y: Float, - width: Float, - height: Float, + x: Float, + y: Float, + width: Float, + height: Float, |}>, |}>; type SvgNodeCommonProps = $ReadOnly<{| name?: string, opacity?: WithDefault, - matrix?: $ReadOnlyArray,//CGAffineTransform, + matrix?: $ReadOnlyArray, //CGAffineTransform, // transform?: ____TransformStyle_Internal, // CATransform3D, custom handling mask?: string, markerStart?: string, @@ -37,17 +37,20 @@ type SvgNodeCommonProps = $ReadOnly<{| type Units = 'userSpaceOnUse' | 'objectBoundingBox'; type NativeProps = $ReadOnly<{| - ...ViewProps, - ...SvgNodeCommonProps, - x1?: string, - y1?: string, - x2?: string, - y2?: string, - gradient?: $ReadOnlyArray, - gradientUnits?: Int32, - gradientTransform?: $ReadOnlyArray,//CGAffineTransform, + ...ViewProps, + ...SvgNodeCommonProps, + x1?: string, + y1?: string, + x2?: string, + y2?: string, + gradient?: $ReadOnlyArray, + gradientUnits?: Int32, + gradientTransform?: $ReadOnlyArray, //CGAffineTransform, |}>; type ComponentType = HostComponent; -export default (codegenNativeComponent('RNSVGLinearGradient', {}): ComponentType); \ No newline at end of file +export default (codegenNativeComponent( + 'RNSVGLinearGradient', + {}, +): ComponentType); diff --git a/src/fabric/MarkerNativeComponent.js b/src/fabric/MarkerNativeComponent.js index 9a1fcf56..59c10d7b 100644 --- a/src/fabric/MarkerNativeComponent.js +++ b/src/fabric/MarkerNativeComponent.js @@ -7,26 +7,24 @@ import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNati import type { HostComponent } from 'react-native/Libraries/Renderer/shims/ReactNativeTypes'; import type { ViewProps } from 'react-native/Libraries/Components/View/ViewPropTypes'; -import type { - DirectEventHandler, -} from 'react-native/Libraries/Types/CodegenTypes'; +import type { DirectEventHandler } from 'react-native/Libraries/Types/CodegenTypes'; // for some reason the parser doesn't allow to put flow types in another file // import type { SvgNodeCommonProps } from './svgNodeCommonProps'; type LayoutEvent = $ReadOnly<{| layout: $ReadOnly<{| - x: Float, - y: Float, - width: Float, - height: Float, + x: Float, + y: Float, + width: Float, + height: Float, |}>, |}>; type SvgNodeCommonProps = $ReadOnly<{| name?: string, opacity?: WithDefault, - matrix?: $ReadOnlyArray,//CGAffineTransform, + matrix?: $ReadOnlyArray, //CGAffineTransform, // transform?: ____TransformStyle_Internal, // CATransform3D, custom handling mask?: string, markerStart?: string, @@ -51,7 +49,7 @@ type SvgRenderableCommonProps = $ReadOnly<{| fillRule?: WithDefault, stroke?: ColorStruct, strokeOpacity?: WithDefault, - strokeWidth?: WithDefault, + strokeWidth?: WithDefault, strokeLinecap?: WithDefault, strokeLinejoin?: WithDefault, strokeDasharray?: $ReadOnlyArray, @@ -62,20 +60,20 @@ type SvgRenderableCommonProps = $ReadOnly<{| |}>; type FontObject = $ReadOnly<{| - fontStyle?: string; - fontVariant?: string; - fontWeight?: string; - fontStretch?: string; - fontSize?: string; - fontFamily?: string; - textAnchor?: string; - textDecoration?: string; - letterSpacing?: string; - wordSpacing?: string; - kerning?: string; - fontFeatureSettings?: string; - fontVariantLigatures?: string; - fontVariationSettings?: string; + fontStyle?: string, + fontVariant?: string, + fontWeight?: string, + fontStretch?: string, + fontSize?: string, + fontFamily?: string, + textAnchor?: string, + textDecoration?: string, + letterSpacing?: string, + wordSpacing?: string, + kerning?: string, + fontFeatureSettings?: string, + fontVariantLigatures?: string, + fontVariationSettings?: string, |}>; type SvgGroupCommonProps = $ReadOnly<{| @@ -105,4 +103,7 @@ type NativeProps = $ReadOnly<{| type ComponentType = HostComponent; -export default (codegenNativeComponent('RNSVGMarker', {}): ComponentType); \ No newline at end of file +export default (codegenNativeComponent( + 'RNSVGMarker', + {}, +): ComponentType); diff --git a/src/fabric/MaskNativeComponent.js b/src/fabric/MaskNativeComponent.js index bc0bb649..c195a615 100644 --- a/src/fabric/MaskNativeComponent.js +++ b/src/fabric/MaskNativeComponent.js @@ -7,26 +7,24 @@ import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNati import type { HostComponent } from 'react-native/Libraries/Renderer/shims/ReactNativeTypes'; import type { ViewProps } from 'react-native/Libraries/Components/View/ViewPropTypes'; -import type { - DirectEventHandler, -} from 'react-native/Libraries/Types/CodegenTypes'; +import type { DirectEventHandler } from 'react-native/Libraries/Types/CodegenTypes'; // for some reason the parser doesn't allow to put flow types in another file // import type { SvgNodeCommonProps } from './svgNodeCommonProps'; type LayoutEvent = $ReadOnly<{| layout: $ReadOnly<{| - x: Float, - y: Float, - width: Float, - height: Float, + x: Float, + y: Float, + width: Float, + height: Float, |}>, |}>; type SvgNodeCommonProps = $ReadOnly<{| name?: string, opacity?: WithDefault, - matrix?: $ReadOnlyArray,//CGAffineTransform, + matrix?: $ReadOnlyArray, //CGAffineTransform, // transform?: ____TransformStyle_Internal, // CATransform3D, custom handling mask?: string, markerStart?: string, @@ -51,7 +49,7 @@ type SvgRenderableCommonProps = $ReadOnly<{| fillRule?: WithDefault, stroke?: ColorStruct, strokeOpacity?: WithDefault, - strokeWidth?: WithDefault, + strokeWidth?: WithDefault, strokeLinecap?: WithDefault, strokeLinejoin?: WithDefault, strokeDasharray?: $ReadOnlyArray, @@ -62,20 +60,20 @@ type SvgRenderableCommonProps = $ReadOnly<{| |}>; type FontObject = $ReadOnly<{| - fontStyle?: string; - fontVariant?: string; - fontWeight?: string; - fontStretch?: string; - fontSize?: string; - fontFamily?: string; - textAnchor?: string; - textDecoration?: string; - letterSpacing?: string; - wordSpacing?: string; - kerning?: string; - fontFeatureSettings?: string; - fontVariantLigatures?: string; - fontVariationSettings?: string; + fontStyle?: string, + fontVariant?: string, + fontWeight?: string, + fontStretch?: string, + fontSize?: string, + fontFamily?: string, + textAnchor?: string, + textDecoration?: string, + letterSpacing?: string, + wordSpacing?: string, + kerning?: string, + fontFeatureSettings?: string, + fontVariantLigatures?: string, + fontVariationSettings?: string, |}>; type SvgGroupCommonProps = $ReadOnly<{| @@ -97,10 +95,12 @@ type NativeProps = $ReadOnly<{| width?: string, maskUnits?: Int32, maskContentUnits?: Int32, - maskTransform?: $ReadOnlyArray,//CGAffineTransform, - + maskTransform?: $ReadOnlyArray, //CGAffineTransform, |}>; type ComponentType = HostComponent; -export default (codegenNativeComponent('RNSVGMask', {}): ComponentType); \ No newline at end of file +export default (codegenNativeComponent( + 'RNSVGMask', + {}, +): ComponentType); diff --git a/src/fabric/PathNativeComponent.js b/src/fabric/PathNativeComponent.js index 773e961b..676744d7 100644 --- a/src/fabric/PathNativeComponent.js +++ b/src/fabric/PathNativeComponent.js @@ -11,17 +11,17 @@ import type { Int32, Double } from 'react-native/Libraries/Types/CodegenTypes'; type LayoutEvent = $ReadOnly<{| layout: $ReadOnly<{| - x: Float, - y: Float, - width: Float, - height: Float, + x: Float, + y: Float, + width: Float, + height: Float, |}>, |}>; type SvgNodeCommonProps = $ReadOnly<{| name?: string, opacity?: WithDefault, - matrix?: $ReadOnlyArray,//CGAffineTransform, + matrix?: $ReadOnlyArray, //CGAffineTransform, // transform?: ____TransformStyle_Internal, // CATransform3D, custom handling mask?: string, markerStart?: string, @@ -46,7 +46,7 @@ type SvgRenderableCommonProps = $ReadOnly<{| fillRule?: WithDefault, stroke?: ColorStruct, strokeOpacity?: WithDefault, - strokeWidth?: WithDefault, + strokeWidth?: WithDefault, strokeLinecap?: WithDefault, strokeLinejoin?: WithDefault, strokeDasharray?: $ReadOnlyArray, @@ -56,14 +56,16 @@ type SvgRenderableCommonProps = $ReadOnly<{| propList?: $ReadOnlyArray, |}>; - type NativeProps = $ReadOnly<{| - ...ViewProps, - ...SvgNodeCommonProps, - ...SvgRenderableCommonProps, - d?: string, + ...ViewProps, + ...SvgNodeCommonProps, + ...SvgRenderableCommonProps, + d?: string, |}>; type ComponentType = HostComponent; -export default (codegenNativeComponent('RNSVGPath', {}): ComponentType); \ No newline at end of file +export default (codegenNativeComponent( + 'RNSVGPath', + {}, +): ComponentType); diff --git a/src/fabric/PatternNativeComponent.js b/src/fabric/PatternNativeComponent.js index cfe4fed2..8fb14f7a 100644 --- a/src/fabric/PatternNativeComponent.js +++ b/src/fabric/PatternNativeComponent.js @@ -7,26 +7,24 @@ import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNati import type { HostComponent } from 'react-native/Libraries/Renderer/shims/ReactNativeTypes'; import type { ViewProps } from 'react-native/Libraries/Components/View/ViewPropTypes'; -import type { - DirectEventHandler, -} from 'react-native/Libraries/Types/CodegenTypes'; +import type { DirectEventHandler } from 'react-native/Libraries/Types/CodegenTypes'; // for some reason the parser doesn't allow to put flow types in another file // import type { SvgNodeCommonProps } from './svgNodeCommonProps'; type LayoutEvent = $ReadOnly<{| layout: $ReadOnly<{| - x: Float, - y: Float, - width: Float, - height: Float, + x: Float, + y: Float, + width: Float, + height: Float, |}>, |}>; type SvgNodeCommonProps = $ReadOnly<{| name?: string, opacity?: WithDefault, - matrix?: $ReadOnlyArray,//CGAffineTransform, + matrix?: $ReadOnlyArray, //CGAffineTransform, // transform?: ____TransformStyle_Internal, // CATransform3D, custom handling mask?: string, markerStart?: string, @@ -51,7 +49,7 @@ type SvgRenderableCommonProps = $ReadOnly<{| fillRule?: WithDefault, stroke?: ColorStruct, strokeOpacity?: WithDefault, - strokeWidth?: WithDefault, + strokeWidth?: WithDefault, strokeLinecap?: WithDefault, strokeLinejoin?: WithDefault, strokeDasharray?: $ReadOnlyArray, @@ -62,20 +60,20 @@ type SvgRenderableCommonProps = $ReadOnly<{| |}>; type FontObject = $ReadOnly<{| - fontStyle?: string; - fontVariant?: string; - fontWeight?: string; - fontStretch?: string; - fontSize?: string; - fontFamily?: string; - textAnchor?: string; - textDecoration?: string; - letterSpacing?: string; - wordSpacing?: string; - kerning?: string; - fontFeatureSettings?: string; - fontVariantLigatures?: string; - fontVariationSettings?: string; + fontStyle?: string, + fontVariant?: string, + fontWeight?: string, + fontStretch?: string, + fontSize?: string, + fontFamily?: string, + textAnchor?: string, + textDecoration?: string, + letterSpacing?: string, + wordSpacing?: string, + kerning?: string, + fontFeatureSettings?: string, + fontVariantLigatures?: string, + fontVariationSettings?: string, |}>; type SvgGroupCommonProps = $ReadOnly<{| @@ -97,7 +95,7 @@ type NativeProps = $ReadOnly<{| width?: string, patternUnits?: Int32, patternContentUnits?: Int32, - patternTransform?: $ReadOnlyArray,//CGAffineTransform, + patternTransform?: $ReadOnlyArray, //CGAffineTransform, minX?: Float, minY?: Float, vbWidth?: Float, @@ -108,4 +106,7 @@ type NativeProps = $ReadOnly<{| type ComponentType = HostComponent; -export default (codegenNativeComponent('RNSVGPattern', {}): ComponentType); \ No newline at end of file +export default (codegenNativeComponent( + 'RNSVGPattern', + {}, +): ComponentType); diff --git a/src/fabric/RadialGradientNativeComponent.js b/src/fabric/RadialGradientNativeComponent.js index f800c90e..29f2ce45 100644 --- a/src/fabric/RadialGradientNativeComponent.js +++ b/src/fabric/RadialGradientNativeComponent.js @@ -11,17 +11,17 @@ import type { Int32, Double } from 'react-native/Libraries/Types/CodegenTypes'; type LayoutEvent = $ReadOnly<{| layout: $ReadOnly<{| - x: Float, - y: Float, - width: Float, - height: Float, + x: Float, + y: Float, + width: Float, + height: Float, |}>, |}>; type SvgNodeCommonProps = $ReadOnly<{| name?: string, opacity?: WithDefault, - matrix?: $ReadOnlyArray,//CGAffineTransform, + matrix?: $ReadOnlyArray, //CGAffineTransform, // transform?: ____TransformStyle_Internal, // CATransform3D, custom handling mask?: string, markerStart?: string, @@ -37,19 +37,22 @@ type SvgNodeCommonProps = $ReadOnly<{| type Units = 'userSpaceOnUse' | 'objectBoundingBox'; type NativeProps = $ReadOnly<{| - ...ViewProps, - ...SvgNodeCommonProps, - fx?: string, - fy?: string, - cx?: string, - cy?: string, - rx?: string, - ry?: string, - gradient?: $ReadOnlyArray, - gradientUnits?: Int32, - gradientTransform?: $ReadOnlyArray,//CGAffineTransform, + ...ViewProps, + ...SvgNodeCommonProps, + fx?: string, + fy?: string, + cx?: string, + cy?: string, + rx?: string, + ry?: string, + gradient?: $ReadOnlyArray, + gradientUnits?: Int32, + gradientTransform?: $ReadOnlyArray, //CGAffineTransform, |}>; type ComponentType = HostComponent; -export default (codegenNativeComponent('RNSVGRadialGradient', {}): ComponentType); \ No newline at end of file +export default (codegenNativeComponent( + 'RNSVGRadialGradient', + {}, +): ComponentType); diff --git a/src/fabric/RectNativeComponent.js b/src/fabric/RectNativeComponent.js index 69f4cc2f..8193c805 100644 --- a/src/fabric/RectNativeComponent.js +++ b/src/fabric/RectNativeComponent.js @@ -11,17 +11,17 @@ import type { Int32, Double } from 'react-native/Libraries/Types/CodegenTypes'; type LayoutEvent = $ReadOnly<{| layout: $ReadOnly<{| - x: Float, - y: Float, - width: Float, - height: Float, + x: Float, + y: Float, + width: Float, + height: Float, |}>, |}>; type SvgNodeCommonProps = $ReadOnly<{| name?: string, opacity?: WithDefault, - matrix?: $ReadOnlyArray,//CGAffineTransform, + matrix?: $ReadOnlyArray, //CGAffineTransform, // transform?: ____TransformStyle_Internal, // CATransform3D, custom handling mask?: string, markerStart?: string, @@ -46,7 +46,7 @@ type SvgRenderableCommonProps = $ReadOnly<{| fillRule?: WithDefault, stroke?: ColorStruct, strokeOpacity?: WithDefault, - strokeWidth?: WithDefault, + strokeWidth?: WithDefault, strokeLinecap?: WithDefault, strokeLinejoin?: WithDefault, strokeDasharray?: $ReadOnlyArray, @@ -56,21 +56,23 @@ type SvgRenderableCommonProps = $ReadOnly<{| propList?: $ReadOnlyArray, |}>; - type NativeProps = $ReadOnly<{| - ...ViewProps, - ...SvgNodeCommonProps, - ...SvgRenderableCommonProps, - x?: string, - y?: string, - rectheight?: string, - rectwidth?: string, - height?: string, - width?: string, - rx?: string, - ry?: string, + ...ViewProps, + ...SvgNodeCommonProps, + ...SvgRenderableCommonProps, + x?: string, + y?: string, + rectheight?: string, + rectwidth?: string, + height?: string, + width?: string, + rx?: string, + ry?: string, |}>; type ComponentType = HostComponent; -export default (codegenNativeComponent('RNSVGRect', {}): ComponentType); \ No newline at end of file +export default (codegenNativeComponent( + 'RNSVGRect', + {}, +): ComponentType); diff --git a/src/fabric/SvgViewNativeComponent.js b/src/fabric/SvgViewNativeComponent.js index b019ad1c..6404bdbe 100644 --- a/src/fabric/SvgViewNativeComponent.js +++ b/src/fabric/SvgViewNativeComponent.js @@ -19,4 +19,7 @@ type NativeProps = $ReadOnly<{| color: ColorValue, |}>; -export default (codegenNativeComponent('RNSVGSvgView', {}): ComponentType); \ No newline at end of file +export default (codegenNativeComponent( + 'RNSVGSvgView', + {}, +): ComponentType); diff --git a/src/fabric/SymbolNativeComponent.js b/src/fabric/SymbolNativeComponent.js index aa74f0bd..5747a5fc 100644 --- a/src/fabric/SymbolNativeComponent.js +++ b/src/fabric/SymbolNativeComponent.js @@ -7,26 +7,24 @@ import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNati import type { HostComponent } from 'react-native/Libraries/Renderer/shims/ReactNativeTypes'; import type { ViewProps } from 'react-native/Libraries/Components/View/ViewPropTypes'; -import type { - DirectEventHandler, -} from 'react-native/Libraries/Types/CodegenTypes'; +import type { DirectEventHandler } from 'react-native/Libraries/Types/CodegenTypes'; // for some reason the parser doesn't allow to put flow types in another file // import type { SvgNodeCommonProps } from './svgNodeCommonProps'; type LayoutEvent = $ReadOnly<{| layout: $ReadOnly<{| - x: Float, - y: Float, - width: Float, - height: Float, + x: Float, + y: Float, + width: Float, + height: Float, |}>, |}>; type SvgNodeCommonProps = $ReadOnly<{| name?: string, opacity?: WithDefault, - matrix?: $ReadOnlyArray,//CGAffineTransform, + matrix?: $ReadOnlyArray, //CGAffineTransform, // transform?: ____TransformStyle_Internal, // CATransform3D, custom handling mask?: string, markerStart?: string, @@ -51,7 +49,7 @@ type SvgRenderableCommonProps = $ReadOnly<{| fillRule?: WithDefault, stroke?: ColorStruct, strokeOpacity?: WithDefault, - strokeWidth?: WithDefault, + strokeWidth?: WithDefault, strokeLinecap?: WithDefault, strokeLinejoin?: WithDefault, strokeDasharray?: $ReadOnlyArray, @@ -62,20 +60,20 @@ type SvgRenderableCommonProps = $ReadOnly<{| |}>; type FontObject = $ReadOnly<{| - fontStyle?: string; - fontVariant?: string; - fontWeight?: string; - fontStretch?: string; - fontSize?: string; - fontFamily?: string; - textAnchor?: string; - textDecoration?: string; - letterSpacing?: string; - wordSpacing?: string; - kerning?: string; - fontFeatureSettings?: string; - fontVariantLigatures?: string; - fontVariationSettings?: string; + fontStyle?: string, + fontVariant?: string, + fontWeight?: string, + fontStretch?: string, + fontSize?: string, + fontFamily?: string, + textAnchor?: string, + textDecoration?: string, + letterSpacing?: string, + wordSpacing?: string, + kerning?: string, + fontFeatureSettings?: string, + fontVariantLigatures?: string, + fontVariationSettings?: string, |}>; type SvgGroupCommonProps = $ReadOnly<{| @@ -99,4 +97,7 @@ type NativeProps = $ReadOnly<{| type ComponentType = HostComponent; -export default (codegenNativeComponent('RNSVGSymbol', {}): ComponentType); \ No newline at end of file +export default (codegenNativeComponent( + 'RNSVGSymbol', + {}, +): ComponentType); diff --git a/src/fabric/TSpanNativeComponent.js b/src/fabric/TSpanNativeComponent.js index 93837b10..a2b22c0b 100644 --- a/src/fabric/TSpanNativeComponent.js +++ b/src/fabric/TSpanNativeComponent.js @@ -11,17 +11,17 @@ import type { Int32, Double } from 'react-native/Libraries/Types/CodegenTypes'; type LayoutEvent = $ReadOnly<{| layout: $ReadOnly<{| - x: Float, - y: Float, - width: Float, - height: Float, + x: Float, + y: Float, + width: Float, + height: Float, |}>, |}>; type SvgNodeCommonProps = $ReadOnly<{| name?: string, opacity?: WithDefault, - matrix?: $ReadOnlyArray,//CGAffineTransform, + matrix?: $ReadOnlyArray, //CGAffineTransform, // transform?: ____TransformStyle_Internal, // CATransform3D, custom handling mask?: string, markerStart?: string, @@ -46,7 +46,7 @@ type SvgRenderableCommonProps = $ReadOnly<{| fillRule?: WithDefault, stroke?: ColorStruct, strokeOpacity?: WithDefault, - strokeWidth?: WithDefault, + strokeWidth?: WithDefault, strokeLinecap?: WithDefault, strokeLinejoin?: WithDefault, strokeDasharray?: $ReadOnlyArray, @@ -57,20 +57,20 @@ type SvgRenderableCommonProps = $ReadOnly<{| |}>; type FontObject = $ReadOnly<{| - fontStyle?: string; - fontVariant?: string; - fontWeight?: string; - fontStretch?: string; - fontSize?: string; - fontFamily?: string; - textAnchor?: string; - textDecoration?: string; - letterSpacing?: string; - wordSpacing?: string; - kerning?: string; - fontFeatureSettings?: string; - fontVariantLigatures?: string; - fontVariationSettings?: string; + fontStyle?: string, + fontVariant?: string, + fontWeight?: string, + fontStretch?: string, + fontSize?: string, + fontFamily?: string, + textAnchor?: string, + textDecoration?: string, + letterSpacing?: string, + wordSpacing?: string, + kerning?: string, + fontFeatureSettings?: string, + fontVariantLigatures?: string, + fontVariationSettings?: string, |}>; type SvgGroupCommonProps = $ReadOnly<{| @@ -102,4 +102,7 @@ type NativeProps = $ReadOnly<{| type ComponentType = HostComponent; -export default (codegenNativeComponent('RNSVGTSpan', {}): ComponentType); \ No newline at end of file +export default (codegenNativeComponent( + 'RNSVGTSpan', + {}, +): ComponentType); diff --git a/src/fabric/TextNativeComponent.js b/src/fabric/TextNativeComponent.js index 2c6d2523..fa2b2d3e 100644 --- a/src/fabric/TextNativeComponent.js +++ b/src/fabric/TextNativeComponent.js @@ -11,17 +11,17 @@ import type { Int32, Double } from 'react-native/Libraries/Types/CodegenTypes'; type LayoutEvent = $ReadOnly<{| layout: $ReadOnly<{| - x: Float, - y: Float, - width: Float, - height: Float, + x: Float, + y: Float, + width: Float, + height: Float, |}>, |}>; type SvgNodeCommonProps = $ReadOnly<{| name?: string, opacity?: WithDefault, - matrix?: $ReadOnlyArray,//CGAffineTransform, + matrix?: $ReadOnlyArray, //CGAffineTransform, // transform?: ____TransformStyle_Internal, // CATransform3D, custom handling mask?: string, markerStart?: string, @@ -46,7 +46,7 @@ type SvgRenderableCommonProps = $ReadOnly<{| fillRule?: WithDefault, stroke?: ColorStruct, strokeOpacity?: WithDefault, - strokeWidth?: WithDefault, + strokeWidth?: WithDefault, strokeLinecap?: WithDefault, strokeLinejoin?: WithDefault, strokeDasharray?: $ReadOnlyArray, @@ -57,20 +57,20 @@ type SvgRenderableCommonProps = $ReadOnly<{| |}>; type FontObject = $ReadOnly<{| - fontStyle?: string; - fontVariant?: string; - fontWeight?: string; - fontStretch?: string; - fontSize?: string; - fontFamily?: string; - textAnchor?: string; - textDecoration?: string; - letterSpacing?: string; - wordSpacing?: string; - kerning?: string; - fontFeatureSettings?: string; - fontVariantLigatures?: string; - fontVariationSettings?: string; + fontStyle?: string, + fontVariant?: string, + fontWeight?: string, + fontStretch?: string, + fontSize?: string, + fontFamily?: string, + textAnchor?: string, + textDecoration?: string, + letterSpacing?: string, + wordSpacing?: string, + kerning?: string, + fontFeatureSettings?: string, + fontVariantLigatures?: string, + fontVariationSettings?: string, |}>; type SvgGroupCommonProps = $ReadOnly<{| @@ -101,4 +101,7 @@ type NativeProps = $ReadOnly<{| type ComponentType = HostComponent; -export default (codegenNativeComponent('RNSVGText', {}): ComponentType); \ No newline at end of file +export default (codegenNativeComponent( + 'RNSVGText', + {}, +): ComponentType); diff --git a/src/fabric/TextPathNativeComponent.js b/src/fabric/TextPathNativeComponent.js index fdc79433..10b15d02 100644 --- a/src/fabric/TextPathNativeComponent.js +++ b/src/fabric/TextPathNativeComponent.js @@ -11,17 +11,17 @@ import type { Int32, Double } from 'react-native/Libraries/Types/CodegenTypes'; type LayoutEvent = $ReadOnly<{| layout: $ReadOnly<{| - x: Float, - y: Float, - width: Float, - height: Float, + x: Float, + y: Float, + width: Float, + height: Float, |}>, |}>; type SvgNodeCommonProps = $ReadOnly<{| name?: string, opacity?: WithDefault, - matrix?: $ReadOnlyArray,//CGAffineTransform, + matrix?: $ReadOnlyArray, //CGAffineTransform, // transform?: ____TransformStyle_Internal, // CATransform3D, custom handling mask?: string, markerStart?: string, @@ -46,7 +46,7 @@ type SvgRenderableCommonProps = $ReadOnly<{| fillRule?: WithDefault, stroke?: ColorStruct, strokeOpacity?: WithDefault, - strokeWidth?: WithDefault, + strokeWidth?: WithDefault, strokeLinecap?: WithDefault, strokeLinejoin?: WithDefault, strokeDasharray?: $ReadOnlyArray, @@ -56,19 +56,21 @@ type SvgRenderableCommonProps = $ReadOnly<{| propList?: $ReadOnlyArray, |}>; - type NativeProps = $ReadOnly<{| - ...ViewProps, - ...SvgNodeCommonProps, - ...SvgRenderableCommonProps, - href?: string, - side?: string, - method?: string, - midLine?: string, - spacing?: string, - startOffset?: string, + ...ViewProps, + ...SvgNodeCommonProps, + ...SvgRenderableCommonProps, + href?: string, + side?: string, + method?: string, + midLine?: string, + spacing?: string, + startOffset?: string, |}>; type ComponentType = HostComponent; -export default (codegenNativeComponent('RNSVGTextPath', {}): ComponentType); \ No newline at end of file +export default (codegenNativeComponent( + 'RNSVGTextPath', + {}, +): ComponentType); diff --git a/src/fabric/UseNativeComponent.js b/src/fabric/UseNativeComponent.js index 2d12c11b..8b2ded61 100644 --- a/src/fabric/UseNativeComponent.js +++ b/src/fabric/UseNativeComponent.js @@ -11,17 +11,17 @@ import type { Int32, Double } from 'react-native/Libraries/Types/CodegenTypes'; type LayoutEvent = $ReadOnly<{| layout: $ReadOnly<{| - x: Float, - y: Float, - width: Float, - height: Float, + x: Float, + y: Float, + width: Float, + height: Float, |}>, |}>; type SvgNodeCommonProps = $ReadOnly<{| name?: string, opacity?: WithDefault, - matrix?: $ReadOnlyArray,//CGAffineTransform, + matrix?: $ReadOnlyArray, //CGAffineTransform, // transform?: ____TransformStyle_Internal, // CATransform3D, custom handling mask?: string, markerStart?: string, @@ -46,7 +46,7 @@ type SvgRenderableCommonProps = $ReadOnly<{| fillRule?: WithDefault, stroke?: ColorStruct, strokeOpacity?: WithDefault, - strokeWidth?: WithDefault, + strokeWidth?: WithDefault, strokeLinecap?: WithDefault, strokeLinejoin?: WithDefault, strokeDasharray?: $ReadOnlyArray, @@ -56,20 +56,22 @@ type SvgRenderableCommonProps = $ReadOnly<{| propList?: $ReadOnlyArray, |}>; - type NativeProps = $ReadOnly<{| - ...ViewProps, - ...SvgNodeCommonProps, - ...SvgRenderableCommonProps, - href?: string, - x?: string, - y?: string, - useheight?: string, - usewidth?: string, - height?: string, - width?: string, + ...ViewProps, + ...SvgNodeCommonProps, + ...SvgRenderableCommonProps, + href?: string, + x?: string, + y?: string, + useheight?: string, + usewidth?: string, + height?: string, + width?: string, |}>; type ComponentType = HostComponent; -export default (codegenNativeComponent('RNSVGUse', {}): ComponentType); \ No newline at end of file +export default (codegenNativeComponent( + 'RNSVGUse', + {}, +): ComponentType); diff --git a/yarn.lock b/yarn.lock index 4ecd04f6..e30c8dde 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2233,7 +2233,7 @@ ansi-escapes@^3.0.0: resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== -ansi-escapes@^4.2.1: +ansi-escapes@^4.2.1, ansi-escapes@^4.3.0: version "4.3.2" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== @@ -2264,6 +2264,11 @@ ansi-regex@^5.0.0, ansi-regex@^5.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== +ansi-regex@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" + integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== + ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -2283,6 +2288,11 @@ ansi-styles@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== +ansi-styles@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.1.0.tgz#87313c102b8118abd57371afab34618bf7350ed3" + integrity sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ== + anymatch@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" @@ -2962,6 +2972,15 @@ cjs-module-lexer@^1.0.0: resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== +clang-format@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/clang-format/-/clang-format-1.8.0.tgz#7779df1c5ce1bc8aac1b0b02b4e479191ef21d46" + integrity sha512-pK8gzfu55/lHzIpQ1givIbWfn3eXnU7SfxqIwVgnn5jEM6j4ZJYjpFqFs4iSBPNedzRMmfjYjuQhu657WAXHXw== + dependencies: + async "^3.2.3" + glob "^7.0.0" + resolve "^1.1.6" + class-utils@^0.3.5: version "0.3.6" resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" @@ -3001,6 +3020,22 @@ cli-spinners@^2.0.0, cli-spinners@^2.2.0, cli-spinners@^2.5.0: resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d" integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g== +cli-truncate@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" + integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg== + dependencies: + slice-ansi "^3.0.0" + string-width "^4.2.0" + +cli-truncate@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-3.1.0.tgz#3f23ab12535e3d73e839bb43e73c9de487db1389" + integrity sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA== + dependencies: + slice-ansi "^5.0.0" + string-width "^5.0.0" + cli-width@^2.0.0: version "2.2.1" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" @@ -3106,6 +3141,11 @@ colorette@^1.0.7: resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40" integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g== +colorette@^2.0.16, colorette@^2.0.17: + version "2.0.19" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798" + integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== + colors@1.4.0, colors@^1.1.2: version "1.4.0" resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" @@ -3133,6 +3173,11 @@ commander@^6.1.0: resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== +commander@^9.3.0: + version "9.3.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-9.3.0.tgz#f619114a5a2d2054e0d9ff1b31d5ccf89255e26b" + integrity sha512-hv95iU5uXPbK83mjrJKuZyFM/LBAoCV/XhVGkS5Je6tl7sxr6A0ITMw5WoRV46/UaJ46Nllm3Xt7IaJhXTIkzw== + commander@~2.13.0: version "2.13.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c" @@ -3612,6 +3657,11 @@ duplexer3@^0.1.4: resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.5.tgz#0b5e4d7bad5de8901ea4440624c8e1d20099217e" integrity sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA== +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" @@ -3646,6 +3696,11 @@ emoji-regex@^8.0.0: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" @@ -4075,6 +4130,21 @@ execa@^4.0.2: signal-exit "^3.0.2" strip-final-newline "^2.0.0" +execa@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-6.1.0.tgz#cea16dee211ff011246556388effa0818394fb20" + integrity sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.1" + human-signals "^3.0.1" + is-stream "^3.0.0" + merge-stream "^2.0.0" + npm-run-path "^5.1.0" + onetime "^6.0.0" + signal-exit "^3.0.7" + strip-final-newline "^3.0.0" + exit@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" @@ -4489,7 +4559,7 @@ get-stream@^5.0.0, get-stream@^5.1.0: dependencies: pump "^3.0.0" -get-stream@^6.0.0: +get-stream@^6.0.0, get-stream@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== @@ -4827,6 +4897,16 @@ human-signals@^2.1.0: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== +human-signals@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-3.0.1.tgz#c740920859dafa50e5a3222da9d3bf4bb0e5eef5" + integrity sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ== + +husky@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/husky/-/husky-8.0.1.tgz#511cb3e57de3e3190514ae49ed50f6bc3f50b3e9" + integrity sha512-xs7/chUH/CKdOCs7Zy0Aev9e/dKOMZf3K1Az1nar3tzlv0jfqnYtu235bstsWTmXOR0EfINrPa97yy4Lz6RiKw== + iconv-lite@0.4.24, iconv-lite@^0.4.17, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -5169,6 +5249,11 @@ is-fullwidth-code-point@^3.0.0: resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== +is-fullwidth-code-point@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz#fae3167c729e7463f8461ce512b080a49268aa88" + integrity sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ== + is-generator-fn@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" @@ -5308,6 +5393,11 @@ is-stream@^2.0.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== +is-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac" + integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== + is-string@^1.0.5, is-string@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" @@ -6135,11 +6225,49 @@ levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" +lilconfig@2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.5.tgz#19e57fd06ccc3848fd1891655b5a447092225b25" + integrity sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg== + lines-and-columns@^1.1.6: version "1.2.4" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== +lint-staged@^13.0.3: + version "13.0.3" + resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-13.0.3.tgz#d7cdf03a3830b327a2b63c6aec953d71d9dc48c6" + integrity sha512-9hmrwSCFroTSYLjflGI8Uk+GWAwMB4OlpU4bMJEAT5d/llQwtYKoim4bLOyLCuWFAhWEupE0vkIFqtw/WIsPug== + dependencies: + cli-truncate "^3.1.0" + colorette "^2.0.17" + commander "^9.3.0" + debug "^4.3.4" + execa "^6.1.0" + lilconfig "2.0.5" + listr2 "^4.0.5" + micromatch "^4.0.5" + normalize-path "^3.0.0" + object-inspect "^1.12.2" + pidtree "^0.6.0" + string-argv "^0.3.1" + yaml "^2.1.1" + +listr2@^4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/listr2/-/listr2-4.0.5.tgz#9dcc50221583e8b4c71c43f9c7dfd0ef546b75d5" + integrity sha512-juGHV1doQdpNT3GSTs9IUN43QJb7KHdF9uqg7Vufs/tG9VTzpFphqF4pm/ICdAABGQxsyNn9CiYA3StkI6jpwA== + dependencies: + cli-truncate "^2.1.0" + colorette "^2.0.16" + log-update "^4.0.0" + p-map "^4.0.0" + rfdc "^1.3.0" + rxjs "^7.5.5" + through "^2.3.8" + wrap-ansi "^7.0.0" + load-json-file@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" @@ -6205,6 +6333,16 @@ log-symbols@^4.1.0: chalk "^4.1.0" is-unicode-supported "^0.1.0" +log-update@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1" + integrity sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg== + dependencies: + ansi-escapes "^4.3.0" + cli-cursor "^3.1.0" + slice-ansi "^4.0.0" + wrap-ansi "^6.2.0" + logkitty@^0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/logkitty/-/logkitty-0.7.1.tgz#8e8d62f4085a826e8d38987722570234e33c6aa7" @@ -6618,7 +6756,7 @@ micromatch@^3.1.10, micromatch@^3.1.4: snapdragon "^0.8.1" to-regex "^3.0.2" -micromatch@^4.0.2, micromatch@^4.0.4: +micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5: version "4.0.5" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== @@ -6658,6 +6796,11 @@ mimic-fn@^2.0.0, mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== +mimic-fn@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" + integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== + mimic-response@^1.0.0, mimic-response@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" @@ -6856,6 +6999,13 @@ npm-run-path@^4.0.0, npm-run-path@^4.0.1: dependencies: path-key "^3.0.0" +npm-run-path@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.1.0.tgz#bc62f7f3f6952d9894bd08944ba011a6ee7b7e00" + integrity sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q== + dependencies: + path-key "^4.0.0" + nth-check@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" @@ -6887,7 +7037,7 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" -object-inspect@^1.12.0, object-inspect@^1.9.0: +object-inspect@^1.12.0, object-inspect@^1.12.2, object-inspect@^1.9.0: version "1.12.2" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ== @@ -6996,6 +7146,13 @@ onetime@^5.1.0, onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" +onetime@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4" + integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ== + dependencies: + mimic-fn "^4.0.0" + open@7.4.2: version "7.4.2" resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321" @@ -7140,6 +7297,13 @@ p-map@^3.0.0: dependencies: aggregate-error "^3.0.0" +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" + p-try@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" @@ -7259,6 +7423,11 @@ path-key@^3.0.0, path-key@^3.1.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== +path-key@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18" + integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== + path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" @@ -7291,6 +7460,11 @@ picomatch@^2.0.4, picomatch@^2.2.3, picomatch@^2.3.1: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== +pidtree@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.6.0.tgz#90ad7b6d42d5841e69e0a2419ef38f8883aa057c" + integrity sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g== + pify@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" @@ -7974,6 +8148,11 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== +rfdc@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" + integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== + rimraf@^2.5.4: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" @@ -8036,7 +8215,7 @@ rxjs@^6.6.0: dependencies: tslib "^1.9.0" -rxjs@^7.2.0: +rxjs@^7.2.0, rxjs@^7.5.5: version "7.5.6" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.6.tgz#0446577557862afd6903517ce7cae79ecb9662bc" integrity sha512-dnyv2/YsXhnm461G+R/Pe5bWP41Nm6LBXEYWI6eiFP4fiwx6WRI/CD0zbdVAudd9xwLEF2IDcKXLHit0FYjUzw== @@ -8303,6 +8482,15 @@ slice-ansi@^2.0.0: astral-regex "^1.0.0" is-fullwidth-code-point "^2.0.0" +slice-ansi@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787" + integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + slice-ansi@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" @@ -8312,6 +8500,14 @@ slice-ansi@^4.0.0: astral-regex "^2.0.0" is-fullwidth-code-point "^3.0.0" +slice-ansi@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-5.0.0.tgz#b73063c57aa96f9cd881654b15294d95d285c42a" + integrity sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ== + dependencies: + ansi-styles "^6.0.0" + is-fullwidth-code-point "^4.0.0" + smart-buffer@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" @@ -8480,6 +8676,11 @@ strict-uri-encode@^2.0.0: resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" integrity sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ== +string-argv@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da" + integrity sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg== + string-length@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" @@ -8510,6 +8711,15 @@ string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2 is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" +string-width@^5.0.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + string.prototype.matchall@^4.0.7: version "4.0.7" resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz#8e6ecb0d8a1fb1fda470d81acecb2dba057a481d" @@ -8582,6 +8792,13 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" +strip-ansi@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2" + integrity sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw== + dependencies: + ansi-regex "^6.0.1" + strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" @@ -8602,6 +8819,11 @@ strip-final-newline@^2.0.0: resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== +strip-final-newline@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" + integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== + strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" @@ -8712,7 +8934,7 @@ through2@^2.0.1: readable-stream "~2.3.6" xtend "~4.0.1" -through@^2.3.6: +through@^2.3.6, through@^2.3.8: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== @@ -9338,6 +9560,11 @@ yaml@1.10.2, yaml@^1.10.0, yaml@^1.7.2: resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== +yaml@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.1.1.tgz#1e06fb4ca46e60d9da07e4f786ea370ed3c3cfec" + integrity sha512-o96x3OPo8GjWeSLF+wOAbrPfhFOGY0W00GNaxCDv+9hkcDJEnev1yh8S7pgHF0ik6zc8sQLuL8hjHjJULZp8bw== + yargs-parser@20.2.9: version "20.2.9" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"