mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-08-30 12:12:40 +08:00
feat(渲染注释): 渲染注释,可配置不渲染
This commit is contained in:
@@ -67,6 +67,7 @@ func NewRenderer(reader *Reader, opts ...RendererOption) *Renderer {
|
||||
r := &Renderer{
|
||||
Reader: reader,
|
||||
DPI: 300.0,
|
||||
RenderAnnotations: true,
|
||||
DrawParams: reader.drawParamCache,
|
||||
CompositeGraphicUnits: reader.compositeGraphicUnitCache,
|
||||
FontMap: make(map[string]*canvas.FontFamily),
|
||||
@@ -87,6 +88,15 @@ func WithDPI(dpi float64) RendererOption {
|
||||
}
|
||||
}
|
||||
|
||||
// WithAnnotations 设置是否渲染注释外观
|
||||
// 入参: enable 是否渲染
|
||||
// 返回: RendererOption 渲染选项
|
||||
func WithAnnotations(enable bool) RendererOption {
|
||||
return func(r *Renderer) {
|
||||
r.RenderAnnotations = enable
|
||||
}
|
||||
}
|
||||
|
||||
// WithFontDirs 设置外部字体查找目录
|
||||
// 入参: dirs 字体目录列表
|
||||
// 返回: RendererOption 渲染选项
|
||||
|
||||
+57
-3
@@ -39,14 +39,68 @@ type Annotations struct {
|
||||
// AnnPage 页面注释引用
|
||||
type AnnPage struct {
|
||||
PageID string `xml:"PageID,attr"`
|
||||
Annotation []Annotation `xml:"Annotation"`
|
||||
FileLoc string `xml:"FileLoc"`
|
||||
}
|
||||
|
||||
// Annotation 注释定义
|
||||
// PageAnnot 页面注释集合
|
||||
type PageAnnot struct {
|
||||
XMLName xml.Name `xml:"PageAnnot"`
|
||||
Annot []Annotation `xml:"Annot"`
|
||||
}
|
||||
|
||||
// Annotation 页面注释
|
||||
type Annotation struct {
|
||||
ID string `xml:"ID,attr"`
|
||||
Type string `xml:"Type,attr"`
|
||||
Subtype string `xml:"Subtype,attr"`
|
||||
Creator string `xml:"Creator,attr"`
|
||||
LastModDate string `xml:"LastModDate,attr"`
|
||||
Loc string `xml:"Loc,attr"`
|
||||
Appearance Appearance
|
||||
}
|
||||
|
||||
// Appearance 注释外观
|
||||
type Appearance struct {
|
||||
Boundary string `xml:"Boundary,attr"`
|
||||
Objects []GraphicObject `xml:"-"`
|
||||
TextObject []TextObject `xml:"TextObject"`
|
||||
PathObject []PathObject `xml:"PathObject"`
|
||||
ImageObject []ImageObject `xml:"ImageObject"`
|
||||
CompositeGraphicUnit []CompositeGraphicUnit `xml:"CompositeGraphicUnit"`
|
||||
}
|
||||
|
||||
// parseAnnotations 解析注释文件
|
||||
// 入参: doc 文档结构
|
||||
// 返回: error 错误信息
|
||||
func (r *Reader) parseAnnotations(doc *Document) error {
|
||||
if doc.Annotations == "" {
|
||||
return nil
|
||||
}
|
||||
annPath := r.ResPath(doc.Annotations)
|
||||
f, err := r.openFile(annPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
var annotations Annotations
|
||||
if err := xml.NewDecoder(f).Decode(&annotations); err != nil {
|
||||
return err
|
||||
}
|
||||
if r.Annots == nil {
|
||||
r.Annots = make(map[string][]Annotation)
|
||||
}
|
||||
for _, page := range annotations.Page {
|
||||
annotPath := resolveResourcePath(annPath, "", page.FileLoc)
|
||||
af, err := r.openFile(annotPath)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
var pageAnnot PageAnnot
|
||||
err = xml.NewDecoder(af).Decode(&pageAnnot)
|
||||
_ = af.Close()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
r.Annots[page.PageID] = append(r.Annots[page.PageID], pageAnnot.Annot...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -123,6 +123,11 @@ func (c *CompositeGraphicUnit) UnmarshalXML(d *xml.Decoder, start xml.StartEleme
|
||||
c.Alpha = &alpha
|
||||
}
|
||||
}
|
||||
if value := attrValue(start, "Visible"); value != "" {
|
||||
if visible, err := strconv.ParseBool(value); err == nil {
|
||||
c.Visible = &visible
|
||||
}
|
||||
}
|
||||
for {
|
||||
tok, err := d.Token()
|
||||
if err != nil {
|
||||
@@ -210,6 +215,93 @@ func (c *CompositeGraphicUnit) decodePageBlock(d *xml.Decoder, start xml.StartEl
|
||||
}
|
||||
}
|
||||
|
||||
// UnmarshalXML 解析注释外观并保留对象顺序
|
||||
// 入参: d XML解码器, start 起始节点
|
||||
// 返回: error 错误信息
|
||||
func (a *Appearance) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||
*a = Appearance{}
|
||||
a.Boundary = attrValue(start, "Boundary")
|
||||
for {
|
||||
tok, err := d.Token()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch node := tok.(type) {
|
||||
case xml.StartElement:
|
||||
if err := a.decodeObject(d, node); err != nil {
|
||||
return err
|
||||
}
|
||||
case xml.EndElement:
|
||||
if node.Name.Local == start.Name.Local {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// decodeObject 解析注释外观子对象
|
||||
// 入参: d XML解码器, start 起始节点
|
||||
// 返回: error 错误信息
|
||||
func (a *Appearance) decodeObject(d *xml.Decoder, start xml.StartElement) error {
|
||||
switch start.Name.Local {
|
||||
case "TextObject":
|
||||
var obj TextObject
|
||||
if err := d.DecodeElement(&obj, &start); err != nil {
|
||||
return err
|
||||
}
|
||||
a.TextObject = append(a.TextObject, obj)
|
||||
a.Objects = append(a.Objects, GraphicObject{Type: start.Name.Local, TextObject: obj})
|
||||
case "PathObject":
|
||||
var obj PathObject
|
||||
if err := d.DecodeElement(&obj, &start); err != nil {
|
||||
return err
|
||||
}
|
||||
a.PathObject = append(a.PathObject, obj)
|
||||
a.Objects = append(a.Objects, GraphicObject{Type: start.Name.Local, PathObject: obj})
|
||||
case "ImageObject":
|
||||
var obj ImageObject
|
||||
if err := d.DecodeElement(&obj, &start); err != nil {
|
||||
return err
|
||||
}
|
||||
a.ImageObject = append(a.ImageObject, obj)
|
||||
a.Objects = append(a.Objects, GraphicObject{Type: start.Name.Local, ImageObject: obj})
|
||||
case "CompositeGraphicUnit", "CompositeObject":
|
||||
var obj CompositeGraphicUnit
|
||||
if err := d.DecodeElement(&obj, &start); err != nil {
|
||||
return err
|
||||
}
|
||||
a.CompositeGraphicUnit = append(a.CompositeGraphicUnit, obj)
|
||||
a.Objects = append(a.Objects, GraphicObject{Type: start.Name.Local, CompositeGraphicUnit: obj})
|
||||
case "PageBlock":
|
||||
return a.decodePageBlock(d, start)
|
||||
default:
|
||||
return d.Skip()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// decodePageBlock 解析注释外观页块子对象
|
||||
// 入参: d XML解码器, start 起始节点
|
||||
// 返回: error 错误信息
|
||||
func (a *Appearance) decodePageBlock(d *xml.Decoder, start xml.StartElement) error {
|
||||
for {
|
||||
tok, err := d.Token()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch node := tok.(type) {
|
||||
case xml.StartElement:
|
||||
if err := a.decodeObject(d, node); err != nil {
|
||||
return err
|
||||
}
|
||||
case xml.EndElement:
|
||||
if node.Name.Local == start.Name.Local {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// attrValue 获取XML属性值
|
||||
// 入参: start 起始节点, name 属性名
|
||||
// 返回: string 属性值
|
||||
|
||||
@@ -88,6 +88,8 @@ type TextObject struct {
|
||||
HScale float64 `xml:"HScale,attr"`
|
||||
VScale float64 `xml:"VScale,attr"`
|
||||
CTM string `xml:"CTM,attr"`
|
||||
Alpha *int `xml:"Alpha,attr"`
|
||||
Visible *bool `xml:"Visible,attr"`
|
||||
Fill *bool `xml:"Fill,attr"`
|
||||
Stroke *bool `xml:"Stroke,attr"`
|
||||
StrokeColor *StrokeColor `xml:"StrokeColor"`
|
||||
@@ -135,6 +137,8 @@ type PathObject struct {
|
||||
DashPattern string `xml:"DashPattern,attr"`
|
||||
MiterLimit float64 `xml:"MiterLimit,attr"`
|
||||
CTM string `xml:"CTM,attr"`
|
||||
Alpha *int `xml:"Alpha,attr"`
|
||||
Visible *bool `xml:"Visible,attr"`
|
||||
Stroke *bool `xml:"Stroke,attr"`
|
||||
Fill *bool `xml:"Fill,attr"`
|
||||
StrokeColor *StrokeColor `xml:"StrokeColor"`
|
||||
@@ -187,5 +191,7 @@ type ImageObject struct {
|
||||
Boundary string `xml:"Boundary,attr"`
|
||||
ResourceID string `xml:"ResourceID,attr"`
|
||||
CTM string `xml:"CTM,attr"`
|
||||
Alpha *int `xml:"Alpha,attr"`
|
||||
Visible *bool `xml:"Visible,attr"`
|
||||
Clips *Clips `xml:"Clips"`
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ type Reader struct {
|
||||
compositeGraphicUnitCache map[string]*CompositeGraphicUnit
|
||||
doc *Document
|
||||
Stamps map[string][]Stamp
|
||||
Annots map[string][]Annotation
|
||||
fileIndex map[string]*zip.File
|
||||
}
|
||||
|
||||
@@ -138,6 +139,7 @@ func (r *Reader) Doc() (*Document, error) {
|
||||
r.loadRes(doc.CommonData.PublicRes)
|
||||
}
|
||||
r.doc = &doc
|
||||
_ = r.parseAnnotations(&doc)
|
||||
_ = r.parseSignatures(&doc)
|
||||
return r.doc, nil
|
||||
}
|
||||
|
||||
+49
-14
@@ -38,6 +38,7 @@ import (
|
||||
type Renderer struct {
|
||||
Reader *Reader
|
||||
DPI float64
|
||||
RenderAnnotations bool
|
||||
fontFamily *canvas.FontFamily
|
||||
DrawParams map[string]*DrawParam
|
||||
CompositeGraphicUnits map[string]*CompositeGraphicUnit
|
||||
@@ -127,6 +128,9 @@ func (r *Renderer) renderPageToContext(ctx *canvas.Context, page *PageContent, d
|
||||
}
|
||||
}
|
||||
}
|
||||
if r.RenderAnnotations {
|
||||
r.renderAnnotations(ctx, page.ID, pageH)
|
||||
}
|
||||
if stamps, ok := r.Reader.Stamps[page.ID]; ok {
|
||||
for _, stamp := range stamps {
|
||||
r.renderStamp(ctx, stamp, pageH)
|
||||
@@ -232,6 +236,21 @@ func (r *Renderer) RenderToMultiPagePDF(writer io.Writer) error {
|
||||
return p.Close()
|
||||
}
|
||||
|
||||
// renderAnnotations 渲染页面注释外观
|
||||
// 入参: ctx 画布上下文, pageID 页面ID, pageH 页面高度
|
||||
func (r *Renderer) renderAnnotations(ctx *canvas.Context, pageID string, pageH float64) {
|
||||
for _, annot := range r.Reader.Annots[pageID] {
|
||||
if strings.EqualFold(annot.Type, "Link") || len(annot.Appearance.Objects) == 0 {
|
||||
continue
|
||||
}
|
||||
box, _ := ParseBox(annot.Appearance.Boundary)
|
||||
ctm := Matrix{a: 1, d: 1, e: box.X, f: box.Y}
|
||||
for _, obj := range annot.Appearance.Objects {
|
||||
r.renderObject(ctx, obj, pageH, nil, nil, 0, &ctm)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// renderTemplate 渲染模板
|
||||
// 入参: ctx 画布上下文, templateID 模板ID, pageH 页面高度
|
||||
func (r *Renderer) renderTemplate(ctx *canvas.Context, templateID string, pageH float64) {
|
||||
@@ -295,6 +314,9 @@ func (r *Renderer) renderLayer(ctx *canvas.Context, layer Layer, pageH float64,
|
||||
// renderCompositeGraphicUnit 渲染复合图元
|
||||
// 入参: ctx 画布上下文, cgu 复合图元对象, pageH 页面高度, defaultFill 默认填充色, defaultStroke 默认描边色, defaultLW 默认线宽, parentCTM 父级CTM
|
||||
func (r *Renderer) renderCompositeGraphicUnit(ctx *canvas.Context, cgu CompositeGraphicUnit, pageH float64, defaultFill, defaultStroke color.Color, defaultLW float64, parentCTM *Matrix) {
|
||||
if cgu.Visible != nil && !*cgu.Visible {
|
||||
return
|
||||
}
|
||||
ctx.Push()
|
||||
currentCTM := NewMatrix(cgu.CTM)
|
||||
if parentCTM != nil {
|
||||
@@ -432,6 +454,9 @@ func (r *Renderer) initCommon() {
|
||||
// renderImage 渲染图片
|
||||
// 入参: ctx 画布上下文, obj 图片对象, pageH 页面高度, parentCTM 父级CTM
|
||||
func (r *Renderer) renderImage(ctx *canvas.Context, obj ImageObject, pageH float64, parentCTM *Matrix) {
|
||||
if obj.Visible != nil && !*obj.Visible {
|
||||
return
|
||||
}
|
||||
resPath, ok := r.Reader.ResMap[obj.ResourceID]
|
||||
if !ok {
|
||||
return
|
||||
@@ -470,6 +495,9 @@ func (r *Renderer) renderImage(ctx *canvas.Context, obj ImageObject, pageH float
|
||||
// renderPath 渲染路径
|
||||
// 入参: ctx 画布上下文, obj 路径对象, pageH 页面高度, defaultFill 默认填充色, defaultStroke 默认描边色, defaultLW 默认线宽, parentCTM 父级CTM
|
||||
func (r *Renderer) renderPath(ctx *canvas.Context, obj PathObject, pageH float64, defaultFill, defaultStroke color.Color, defaultLW float64, parentCTM *Matrix) {
|
||||
if obj.Visible != nil && !*obj.Visible {
|
||||
return
|
||||
}
|
||||
ctx.Push()
|
||||
bx, by := 0.0, 0.0
|
||||
if obj.Boundary != "" {
|
||||
@@ -499,12 +527,14 @@ func (r *Renderer) renderPath(ctx *canvas.Context, obj PathObject, pageH float64
|
||||
lineWidth = dp.LineWidth
|
||||
}
|
||||
if dp.FillColor != nil {
|
||||
fillColor = parseFillColor(dp.FillColor)
|
||||
fillPaint = parseFillPaint(dp.FillColor, bx, by, pageH, 0, 0)
|
||||
fillColorNode := withFillAlpha(dp.FillColor, obj.Alpha)
|
||||
fillColor = parseFillColor(fillColorNode)
|
||||
fillPaint = parseFillPaint(fillColorNode, bx, by, pageH, 0, 0)
|
||||
}
|
||||
if dp.StrokeColor != nil {
|
||||
strokeColor = parseStrokeColor(dp.StrokeColor)
|
||||
strokePaint = parseStrokePaint(dp.StrokeColor, bx, by, pageH, 0, 0)
|
||||
strokeColorNode := withStrokeAlpha(dp.StrokeColor, obj.Alpha)
|
||||
strokeColor = parseStrokeColor(strokeColorNode)
|
||||
strokePaint = parseStrokePaint(strokeColorNode, bx, by, pageH, 0, 0)
|
||||
}
|
||||
if dp.Cap == "Round" {
|
||||
lineCap = canvas.RoundCap
|
||||
@@ -526,12 +556,14 @@ func (r *Renderer) renderPath(ctx *canvas.Context, obj PathObject, pageH float64
|
||||
lineWidth = obj.LineWidth
|
||||
}
|
||||
if obj.FillColor != nil {
|
||||
fillColor = parseFillColor(obj.FillColor)
|
||||
fillPaint = parseFillPaint(obj.FillColor, bx, by, pageH, 0, 0)
|
||||
fillColorNode := withFillAlpha(obj.FillColor, obj.Alpha)
|
||||
fillColor = parseFillColor(fillColorNode)
|
||||
fillPaint = parseFillPaint(fillColorNode, bx, by, pageH, 0, 0)
|
||||
}
|
||||
if obj.StrokeColor != nil {
|
||||
strokeColor = parseStrokeColor(obj.StrokeColor)
|
||||
strokePaint = parseStrokePaint(obj.StrokeColor, bx, by, pageH, 0, 0)
|
||||
strokeColorNode := withStrokeAlpha(obj.StrokeColor, obj.Alpha)
|
||||
strokeColor = parseStrokeColor(strokeColorNode)
|
||||
strokePaint = parseStrokePaint(strokeColorNode, bx, by, pageH, 0, 0)
|
||||
}
|
||||
if obj.Cap != "" {
|
||||
if obj.Cap == "Round" {
|
||||
@@ -624,6 +656,9 @@ func (r *Renderer) renderPath(ctx *canvas.Context, obj PathObject, pageH float64
|
||||
// renderText 渲染文本
|
||||
// 入参: ctx 画布上下文, obj 文本对象, pageH 页面高度, defaultFill 默认填充色, defaultStroke 默认描边色, parentCTM 父级CTM
|
||||
func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64, defaultFill, defaultStroke color.Color, parentCTM *Matrix) {
|
||||
if obj.Visible != nil && !*obj.Visible {
|
||||
return
|
||||
}
|
||||
ctx.Push()
|
||||
bx, by := 0.0, 0.0
|
||||
if obj.Boundary != "" {
|
||||
@@ -662,14 +697,14 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64
|
||||
var fillPaint any = fillColor
|
||||
var fillColorNode *FillColor
|
||||
if dp != nil && dp.FillColor != nil {
|
||||
fillColor = parseFillColor(dp.FillColor)
|
||||
fillColorNode = dp.FillColor
|
||||
fillPaint = parseFillPaint(dp.FillColor, bx, by, pageH, 0, 0)
|
||||
fillColorNode = withFillAlpha(dp.FillColor, obj.Alpha)
|
||||
fillColor = parseFillColor(fillColorNode)
|
||||
fillPaint = parseFillPaint(fillColorNode, bx, by, pageH, 0, 0)
|
||||
}
|
||||
if obj.FillColor != nil {
|
||||
fillColor = parseFillColor(obj.FillColor)
|
||||
fillColorNode = obj.FillColor
|
||||
fillPaint = parseFillPaint(obj.FillColor, bx, by, pageH, 0, 0)
|
||||
fillColorNode = withFillAlpha(obj.FillColor, obj.Alpha)
|
||||
fillColor = parseFillColor(fillColorNode)
|
||||
fillPaint = parseFillPaint(fillColorNode, bx, by, pageH, 0, 0)
|
||||
}
|
||||
if fillPaint == nil {
|
||||
fillPaint = fillColor
|
||||
|
||||
@@ -97,6 +97,7 @@ type CompositeGraphicUnit struct {
|
||||
CTM string `xml:"CTM,attr"`
|
||||
DrawParam string `xml:"DrawParam,attr"`
|
||||
Alpha *int `xml:"Alpha,attr"`
|
||||
Visible *bool `xml:"Visible,attr"`
|
||||
Objects []GraphicObject `xml:"-"`
|
||||
TextObject []TextObject `xml:"TextObject"`
|
||||
PathObject []PathObject `xml:"PathObject"`
|
||||
|
||||
+65
-1
@@ -42,11 +42,72 @@ func parseColorWithAlpha(val string, alpha *int) color.Color {
|
||||
if alpha != nil {
|
||||
a = *alpha
|
||||
}
|
||||
return color.RGBA{R: uint8(r), G: uint8(g), B: uint8(b), A: uint8(a)}
|
||||
a = clampColor(a)
|
||||
return color.RGBA{
|
||||
R: uint8(clampColor(r) * a / 255),
|
||||
G: uint8(clampColor(g) * a / 255),
|
||||
B: uint8(clampColor(b) * a / 255),
|
||||
A: uint8(a),
|
||||
}
|
||||
}
|
||||
return color.Black
|
||||
}
|
||||
|
||||
// clampColor 限制颜色分量范围
|
||||
// 入参: v 颜色分量
|
||||
// 返回: int 颜色分量
|
||||
func clampColor(v int) int {
|
||||
if v < 0 {
|
||||
return 0
|
||||
}
|
||||
if v > 255 {
|
||||
return 255
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// mergeAlpha 合并透明度
|
||||
// 入参: colorAlpha 颜色透明度, objectAlpha 对象透明度
|
||||
// 返回: *int 合并后的透明度
|
||||
func mergeAlpha(colorAlpha, objectAlpha *int) *int {
|
||||
if colorAlpha == nil && objectAlpha == nil {
|
||||
return nil
|
||||
}
|
||||
alpha := 255
|
||||
if colorAlpha != nil {
|
||||
alpha = *colorAlpha
|
||||
}
|
||||
if objectAlpha != nil {
|
||||
alpha = alpha * *objectAlpha / 255
|
||||
}
|
||||
alpha = clampColor(alpha)
|
||||
return &alpha
|
||||
}
|
||||
|
||||
// withFillAlpha 合并填充色透明度
|
||||
// 入参: fillColor 填充颜色节点, alpha 对象透明度
|
||||
// 返回: *FillColor 合并后的填充颜色节点
|
||||
func withFillAlpha(fillColor *FillColor, alpha *int) *FillColor {
|
||||
if fillColor == nil || alpha == nil {
|
||||
return fillColor
|
||||
}
|
||||
merged := *fillColor
|
||||
merged.Alpha = mergeAlpha(fillColor.Alpha, alpha)
|
||||
return &merged
|
||||
}
|
||||
|
||||
// withStrokeAlpha 合并勾边色透明度
|
||||
// 入参: strokeColor 勾边颜色节点, alpha 对象透明度
|
||||
// 返回: *StrokeColor 合并后的勾边颜色节点
|
||||
func withStrokeAlpha(strokeColor *StrokeColor, alpha *int) *StrokeColor {
|
||||
if strokeColor == nil || alpha == nil {
|
||||
return strokeColor
|
||||
}
|
||||
merged := *strokeColor
|
||||
merged.Alpha = mergeAlpha(strokeColor.Alpha, alpha)
|
||||
return &merged
|
||||
}
|
||||
|
||||
// parseFillColor 解析填充颜色
|
||||
// 入参: fillColor 填充颜色节点
|
||||
// 返回: color.Color 颜色对象
|
||||
@@ -228,6 +289,9 @@ func parseRadialShdGradient(radialShd *RadialShd, alpha *int, x, y, pageH, origi
|
||||
// 入参: c 颜色对象
|
||||
// 返回: color.RGBA RGBA颜色
|
||||
func colorToRGBA(c color.Color) color.RGBA {
|
||||
if rgba, ok := c.(color.RGBA); ok {
|
||||
return rgba
|
||||
}
|
||||
r, g, b, a := c.RGBA()
|
||||
return color.RGBA{R: uint8(r >> 8), G: uint8(g >> 8), B: uint8(b >> 8), A: uint8(a >> 8)}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user