@@ -606,4 +606,291 @@ class FontInfo {
606606 }
607607}
608608
609- export { CssFontInfo , FontInfo , SystemFontInfo } ;
609+ class PatternInfo {
610+ static #KIND = 0 ; // 1=axial, 2=radial, 3=mesh
611+
612+ static #HAS_BBOX = 1 ; // 0/1
613+
614+ static #HAS_BACKGROUND = 2 ; // 0/1 (background for mesh patterns)
615+
616+ static #SHADING_TYPE = 3 ; // shadingType (only for mesh patterns)
617+
618+ static #N_COORD = 4 ; // number of coordinate pairs
619+
620+ static #N_COLOR = 8 ; // number of rgb triplets
621+
622+ static #N_STOP = 12 ; // number of gradient stops
623+
624+ static #N_FIGURES = 16 ; // number of figures
625+
626+ constructor ( buffer ) {
627+ this . buffer = buffer ;
628+ this . view = new DataView ( buffer ) ;
629+ this . data = new Uint8Array ( buffer ) ;
630+ }
631+
632+ static write ( ir ) {
633+ let kind ,
634+ bbox = null ,
635+ coords = [ ] ,
636+ colors = [ ] ,
637+ colorStops = [ ] ,
638+ figures = [ ] ,
639+ shadingType = null , // only needed for mesh patterns
640+ background = null ; // background for mesh patterns
641+
642+ switch ( ir [ 0 ] ) {
643+ case "RadialAxial" :
644+ kind = ir [ 1 ] === "axial" ? 1 : 2 ;
645+ bbox = ir [ 2 ] ;
646+ colorStops = ir [ 3 ] ;
647+ if ( kind === 1 ) {
648+ coords . push ( ...ir [ 4 ] , ...ir [ 5 ] ) ;
649+ } else {
650+ coords . push ( ir [ 4 ] [ 0 ] , ir [ 4 ] [ 1 ] , ir [ 6 ] , ir [ 5 ] [ 0 ] , ir [ 5 ] [ 1 ] , ir [ 7 ] ) ;
651+ }
652+ break ;
653+ case "Mesh" :
654+ kind = 3 ;
655+ shadingType = ir [ 1 ] ;
656+ coords = ir [ 2 ] ;
657+ colors = ir [ 3 ] ;
658+ figures = ir [ 4 ] || [ ] ;
659+ bbox = ir [ 6 ] ;
660+ background = ir [ 7 ] ;
661+ break ;
662+ default :
663+ throw new Error ( `Unsupported pattern type: ${ ir [ 0 ] } ` ) ;
664+ }
665+
666+ const nCoord = Math . floor ( coords . length / 2 ) ;
667+ const nColor = Math . floor ( colors . length / 3 ) ;
668+ const nStop = colorStops . length ;
669+ const nFigures = figures . length ;
670+
671+ const encoder = new TextEncoder ( ) ;
672+ const encodedFigureTypes = [ ] ;
673+ let figuresSize = 0 ;
674+ for ( const figure of figures ) {
675+ figuresSize += 4 ;
676+ const typeBytes = encoder . encode ( figure . type ) ;
677+ encodedFigureTypes . push ( typeBytes ) ;
678+ figuresSize += typeBytes . length ;
679+ figuresSize = Math . ceil ( figuresSize / 4 ) * 4 ; // Ensure 4-byte alignment
680+ figuresSize += 4 + figure . coords . length * 4 ;
681+ figuresSize += 4 + figure . colors . length * 4 ;
682+ if ( figure . verticesPerRow !== undefined ) {
683+ figuresSize += 4 ;
684+ }
685+ }
686+
687+ const byteLen =
688+ 20 +
689+ nCoord * 8 +
690+ nColor * 3 +
691+ nStop * 8 +
692+ ( bbox ? 16 : 0 ) +
693+ ( background ? 3 : 0 ) +
694+ figuresSize ;
695+ const buffer = new ArrayBuffer ( byteLen ) ;
696+ const dataView = new DataView ( buffer ) ;
697+ const u8data = new Uint8Array ( buffer ) ;
698+
699+ dataView . setUint8 ( PatternInfo . #KIND, kind ) ;
700+ dataView . setUint8 ( PatternInfo . #HAS_BBOX, bbox ? 1 : 0 ) ;
701+ dataView . setUint8 ( PatternInfo . #HAS_BACKGROUND, background ? 1 : 0 ) ;
702+ dataView . setUint8 ( PatternInfo . #SHADING_TYPE, shadingType ) ; // Only for mesh pattern, null otherwise
703+ dataView . setUint32 ( PatternInfo . #N_COORD, nCoord , true ) ;
704+ dataView . setUint32 ( PatternInfo . #N_COLOR, nColor , true ) ;
705+ dataView . setUint32 ( PatternInfo . #N_STOP, nStop , true ) ;
706+ dataView . setUint32 ( PatternInfo . #N_FIGURES, nFigures , true ) ;
707+
708+ let offset = 20 ;
709+ const coordsView = new Float32Array ( buffer , offset , nCoord * 2 ) ;
710+ coordsView . set ( coords ) ;
711+ offset += nCoord * 8 ;
712+
713+ u8data . set ( colors , offset ) ;
714+ offset += nColor * 3 ;
715+
716+ for ( const [ pos , hex ] of colorStops ) {
717+ dataView . setFloat32 ( offset , pos , true ) ;
718+ offset += 4 ;
719+ dataView . setUint32 ( offset , parseInt ( hex . slice ( 1 ) , 16 ) , true ) ;
720+ offset += 4 ;
721+ }
722+ if ( bbox ) {
723+ for ( const v of bbox ) {
724+ dataView . setFloat32 ( offset , v , true ) ;
725+ offset += 4 ;
726+ }
727+ }
728+
729+ if ( background ) {
730+ u8data . set ( background , offset ) ;
731+ offset += 3 ;
732+ }
733+
734+ for ( let i = 0 ; i < figures . length ; i ++ ) {
735+ const figure = figures [ i ] ;
736+ const typeBytes = encodedFigureTypes [ i ] ;
737+ dataView . setUint32 ( offset , typeBytes . length , true ) ;
738+ offset += 4 ;
739+ u8data . set ( typeBytes , offset ) ;
740+ offset += typeBytes . length ;
741+ // Ensure 4-byte alignment
742+ offset = Math . ceil ( offset / 4 ) * 4 ;
743+ dataView . setUint32 ( offset , figure . coords . length , true ) ;
744+ offset += 4 ;
745+ const figureCoordsView = new Int32Array (
746+ buffer ,
747+ offset ,
748+ figure . coords . length
749+ ) ;
750+ figureCoordsView . set ( figure . coords ) ;
751+ offset += figure . coords . length * 4 ;
752+ dataView . setUint32 ( offset , figure . colors . length , true ) ;
753+ offset += 4 ;
754+ const colorsView = new Int32Array ( buffer , offset , figure . colors . length ) ;
755+ colorsView . set ( figure . colors ) ;
756+ offset += figure . colors . length * 4 ;
757+
758+ if ( figure . verticesPerRow !== undefined ) {
759+ dataView . setUint32 ( offset , figure . verticesPerRow , true ) ;
760+ offset += 4 ;
761+ }
762+ }
763+ return buffer ;
764+ }
765+
766+ getIR ( ) {
767+ const dataView = this . view ;
768+ const kind = this . data [ PatternInfo . #KIND] ;
769+ const hasBBox = ! ! this . data [ PatternInfo . #HAS_BBOX] ;
770+ const hasBackground = ! ! this . data [ PatternInfo . #HAS_BACKGROUND] ;
771+ const nCoord = dataView . getUint32 ( PatternInfo . #N_COORD, true ) ;
772+ const nColor = dataView . getUint32 ( PatternInfo . #N_COLOR, true ) ;
773+ const nStop = dataView . getUint32 ( PatternInfo . #N_STOP, true ) ;
774+ const nFigures = dataView . getUint32 ( PatternInfo . #N_FIGURES, true ) ;
775+
776+ let offset = 20 ;
777+ const coords = new Float32Array ( this . buffer , offset , nCoord * 2 ) ;
778+ offset += nCoord * 8 ;
779+ const colors = new Uint8Array ( this . buffer , offset , nColor * 3 ) ;
780+ offset += nColor * 3 ;
781+ const stops = [ ] ;
782+ for ( let i = 0 ; i < nStop ; ++ i ) {
783+ const p = dataView . getFloat32 ( offset , true ) ;
784+ offset += 4 ;
785+ const rgb = dataView . getUint32 ( offset , true ) ;
786+ offset += 4 ;
787+ stops . push ( [ p , `#${ rgb . toString ( 16 ) . padStart ( 6 , "0" ) } ` ] ) ;
788+ }
789+ let bbox = null ;
790+ if ( hasBBox ) {
791+ bbox = [ ] ;
792+ for ( let i = 0 ; i < 4 ; ++ i ) {
793+ bbox . push ( dataView . getFloat32 ( offset , true ) ) ;
794+ offset += 4 ;
795+ }
796+ }
797+
798+ let background = null ;
799+ if ( hasBackground ) {
800+ background = new Uint8Array ( this . buffer , offset , 3 ) ;
801+ offset += 3 ;
802+ }
803+
804+ const figures = [ ] ;
805+ const decoder = new TextDecoder ( ) ;
806+ for ( let i = 0 ; i < nFigures ; ++ i ) {
807+ const typeLength = dataView . getUint32 ( offset , true ) ;
808+ offset += 4 ;
809+ const type = decoder . decode (
810+ new Uint8Array ( this . buffer , offset , typeLength )
811+ ) ;
812+ offset += typeLength ;
813+ offset = Math . ceil ( offset / 4 ) * 4 ;
814+
815+ const coordsLength = dataView . getUint32 ( offset , true ) ;
816+ offset += 4 ;
817+ const figureCoords = new Int32Array ( this . buffer , offset , coordsLength ) ;
818+ offset += coordsLength * 4 ;
819+
820+ const colorsLength = dataView . getUint32 ( offset , true ) ;
821+ offset += 4 ;
822+ const figureColors = new Int32Array ( this . buffer , offset , colorsLength ) ;
823+ offset += colorsLength * 4 ;
824+
825+ const figure = {
826+ type,
827+ coords : figureCoords ,
828+ colors : figureColors ,
829+ } ;
830+
831+ if ( type === "lattice" ) {
832+ figure . verticesPerRow = dataView . getUint32 ( offset , true ) ;
833+ offset += 4 ;
834+ }
835+
836+ figures . push ( figure ) ;
837+ }
838+
839+ if ( kind === 1 ) {
840+ // axial
841+ return [
842+ "RadialAxial" ,
843+ "axial" ,
844+ bbox ,
845+ stops ,
846+ Array . from ( coords . slice ( 0 , 2 ) ) ,
847+ Array . from ( coords . slice ( 2 , 4 ) ) ,
848+ null ,
849+ null ,
850+ ] ;
851+ }
852+ if ( kind === 2 ) {
853+ return [
854+ "RadialAxial" ,
855+ "radial" ,
856+ bbox ,
857+ stops ,
858+ [ coords [ 0 ] , coords [ 1 ] ] ,
859+ [ coords [ 3 ] , coords [ 4 ] ] ,
860+ coords [ 2 ] ,
861+ coords [ 5 ] ,
862+ ] ;
863+ }
864+ if ( kind === 3 ) {
865+ const shadingType = this . data [ PatternInfo . #SHADING_TYPE] ;
866+ let bounds = null ;
867+ if ( coords . length > 0 ) {
868+ let minX = coords [ 0 ] ,
869+ maxX = coords [ 0 ] ;
870+ let minY = coords [ 1 ] ,
871+ maxY = coords [ 1 ] ;
872+ for ( let i = 0 ; i < coords . length ; i += 2 ) {
873+ const x = coords [ i ] ,
874+ y = coords [ i + 1 ] ;
875+ minX = minX > x ? x : minX ;
876+ minY = minY > y ? y : minY ;
877+ maxX = maxX < x ? x : maxX ;
878+ maxY = maxY < y ? y : maxY ;
879+ }
880+ bounds = [ minX , minY , maxX , maxY ] ;
881+ }
882+ return [
883+ "Mesh" ,
884+ shadingType ,
885+ coords ,
886+ colors ,
887+ figures ,
888+ bounds ,
889+ bbox ,
890+ background ,
891+ ] ;
892+ }
893+ throw new Error ( `Unsupported pattern kind: ${ kind } ` ) ;
894+ }
895+ }
896+ export { CssFontInfo , FontInfo , PatternInfo , SystemFontInfo } ;
0 commit comments