Core Text (一) String Attribute Name, Glyph

本文介绍了CoreText的基本使用,包括创建属性字符串NSAttributedString和文本绘制区域CTFrameRef,强调了CoreText在图文混排中的角色,以及如何设置文本属性。

参考文章:http://blog.youkuaiyun.com/fengsh998/article/details/8691823


目前在研究CoreText相关技术,顺便写一点笔记和心得,由于经验有限,有些东西只是个人的猜想,不一定正确,望指正 。。。



1,对core text框架的一点理解(整理中。。。。)

core text其实只是一个文本布局渲染框架,它并不负责绘制文本,它的主要功能就是设置文本(字符串)的各种绘制属性以及布局信息,如字体、颜色、字形、段落样式等属性以及绘制区域等布局信息,最后通过core graphic框架将文本绘制出来。

在core text中,起关键作用的一个对象是:NSAttributedString(CFAttributedStringRef)和CTFrameRef, AttributedString存储了需要绘制的文本内容,以及文本的绘制属性;CTFrameRef负责管理文本的布局信息。


所以CoreText的使用主要有两个部分:1 创建属性字符串,并且设置属性 NSAttributedString; 2 创建文本绘制区域CTFrameRef。

图文混排:由于CoreText只是一个文本渲染框架,它只负责渲染文本内容,而决然不会去负责图片的绘制。图文混排,以及超链接的实现,其实是先通过特定的方式标记出需要特殊处理的文本,然后通过CTFrameRef计算出目标文本域的位置,然后再进行相应的处理,如绘制背景,设置触摸响应等。


 

2,core text设置文本属性的属性名解析

String Attribute Name

const CFStringRef kCTCharacterShapeAttributeName;//CFNumberRef对象,默认为0
const CFStringRef kCTFontAttributeName;//字体 CTFont  Default is Helvetica 12. 与UIFont的默认值不同
const CFStringRef kCTKernAttributeName;//字符间隔属性,必须是CFNumberRef对象
const CFStringRef kCTLigatureAttributeName;//设置使用连字符属性。CFNumberRef对象,默认为1,表示使用标准的连字。可取值:0,1,2。A value of0 indicates that only ligatures essential for proper rendering of text should be used. A value of 1 indicates that standard ligatures should be used, and 2 indicates that all available ligatures should be used. Which ligatures are standard depends on the script and possibly the font.
const CFStringRef kCTForegroundColorAttributeName;//CGColor. default is black.
const CFStringRef kCTForegroundColorFromContextAttributeName;//上下文字体颜色 CFBoolean, 默认为false。If set, this attribute also determines the color used bykCTUnderlineStyleAttributeName, in which case it overrides the foreground color.
const CFStringRef kCTParagraphStyleAttributeName;//段落样式属性。必须是CTParagraphStyle,默认为nil
const CFStringRef kCTStrokeWidthAttributeName;//笔画线条宽度CFNumberRef,默认是0.0f,标准是3.0f
const CFStringRef kCTStrokeColorAttributeName;//笔画颜色,默认为与前景色相同
const CFStringRef kCTSuperscriptAttributeName;//设置字体的上下标属性 必须是CFNumberRef对象 默认为0,-1为下标,1为上标,需要字体支持。
 
const CFStringRef kCTUnderlineColorAttributeName;//默认与前景色相同 const CFStringRef kCTUnderlineStyleAttributeName;//CFNumberRef(CTUnderlineStyle,CTUnderlineStyleModifiers)
enum{
kCTUnderlineStyleNone = 0x00,
kCTUnderlineStyleSingle = 0x01,
kCTUnderlineStyleThick = 0x02,
kCTUnderlineStyleDouble = 0x09
};
typedef int32_t CTUnderlineStyle;
enum{
kCTUnderlinePatternSolid = 0x0000,
kCTUnderlinePatternDot = 0x0100,
kCTUnderlinePatternDash = 0x0200,
kCTUnderlinePatternDashDot = 0x0300,
kCTUnderlinePatternDashDotDot = 0x0400
};
typedef int32_t CTUnderlineStyleModifiers;
 

const CFStringRef kCTVerticalFormsAttributeName;//文字方向,默认为false,表示水平方向。
const CFStringRef kCTGlyphInfoAttributeName;//字体信息属性,必须为CTGlyphInfo对象
const CFStringRef kCTRunDelegateAttributeName//CTRun的代理,必须是CTRunDelegate对象

------------------------------------------以下纯属搬运工----------------------------------

字体的基本知识:

字体(Font):是一系列字号、样式和磅值相同的字符(例如:10磅黑体Palatino)。现多被视为字样的同义词

字面(Face):是所有字号的磅值和格式的综合

字体集(Font family):是一组相关字体(例如:Franklin family包括Franklin Gothic、Fran-klinHeavy和Franklin Compressed)

磅值(Weight):用于描述字体粗度。典型的磅值,从最粗到最细,有极细、细、book、中等、半粗、粗、较粗、极粗

样式(Style):字形有三种形式:Roman type是直体;oblique type是斜体;utakuc type是斜体兼曲线(比Roman type更像书法体)。

x高度(X height):指小写字母的平均高度(以x为基准)。磅值相同的两字母,x高度越大的字母看起来比x高度小的字母要大

Cap高度(Cap height):与x高度相似。指大写字母的平均高度(以C为基准)

下行字母(Descender):例如在字母q中,基线以下的字母部分叫下伸部分

上行字母(Ascender):x高度以上的部分(比如字母b)叫做上伸部分

基线(Baseline):通常在x、v、b、m下的那条线

描边(Stroke):组成字符的线或曲线。可以加粗或改变字符形状

衬线(Serif):用来使字符更可视的一条水平线。如字母左上角和下部的水平线。

无衬线(Sans Serif):可以让排字员不使用衬线装饰。

方形字(Block):这种字体的笔画使字符看起来比无衬线字更显眼,但还不到常见的衬线字的程度。例如Lubalin Graph就是方形字,这种字看起来好像是木头块刻的一样

手写体脚本(Calligraphic script):是一种仿效手写体的字体。例如Murray Hill或者Fraktur字体

艺术字(Decorative):像绘画般的字体

Pi符号(Pisymbol):非标准的字母数字字符的特殊符号。例如Wingdings和Mathematical Pi

连写(Ligature):是一系列连写字母如fi、fl、ffi或ffl。由于字些字母形状的原因经常被连写,故排字员已习惯将它们连写。


import tkinter as tk import time import sys import os import platform import ctypes import csv import threading import numpy as np import matplotlib.pyplot as plt from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg from ctypes import windll, wintypes from collections import defaultdict from datetime import datetime class HighPrecisionTimer: """高精度计时器基类""" def __init__(self): self.start_time = 0 self.total_elapsed = 0 self.running = False self.interrupt_count = 0 # 中断计数器 self.core_usage = defaultdict(float) # 各核心使用时间 def start(self): """开始计时""" if not self.running: self.start_time = self.current_time() self.running = True def stop(self): """停止计时""" if self.running: elapsed = self.current_time() - self.start_time self.total_elapsed += elapsed self.update_core_usage(elapsed) # 更新核心使用情况 self.running = False def reset(self): """重置计时器""" self.total_elapsed = 0 self.running = False self.interrupt_count = 0 self.core_usage.clear() def current_elapsed(self): """获取当前已过时间(毫秒)""" if self.running: return self.total_elapsed + (self.current_time() - self.start_time) return self.total_elapsed def current_time(self): """抽象方法,由子类实现具体计时方案""" raise NotImplementedError("Subclasses must implement current_time()") def update_core_usage(self, elapsed): """更新核心使用情况""" # 此处为简化实现,实际项目中应使用系统API获取各核心使用数据 if platform.system() == 'Windows': # 模拟多核使用情况 for core in range(os.cpu_count()): self.core_usage[core] += elapsed * (0.3 + 0.6 * np.random.random()) else: # Linux系统可读取/proc/stat获取实际数据 self.core_usage[0] = elapsed # 默认单核心 def get_cpu_utilization(self, physical_time): if physical_time > 0: return min(100, max(0, (self.current_elapsed() / physical_time) * 100)) return 0 def record_interrupt(self): """记录中断事件""" self.interrupt_count += 1 class PerformanceCounterTimer(HighPrecisionTimer): """基于性能计数器的物理计时方案""" def __init__(self): super().__init__() # 获取性能计数器频率 if platform.system() == 'Windows': self.frequency = ctypes.c_int64() windll.kernel32.QueryPerformanceFrequency(ctypes.byref(self.frequency)) self.frequency = self.frequency.value else: # Linux/macOS使用time.perf_counter self.frequency = 1e9 # 1秒 = 1e9纳秒 def current_time(self): """获取当前性能计数器时间(毫秒)""" if platform.system() == 'Windows': counter = ctypes.c_int64() windll.kernel32.QueryPerformanceCounter(ctypes.byref(counter)) return (counter.value * 1000) / self.frequency # 转换为毫秒 else: # 跨平台方案 return time.perf_counter() * 1000 class CPUCycleTimer(HighPrecisionTimer): """基于CPU周期的计时方案""" def __init__(self): super().__init__() # 获取CPU频率 self.frequency = self.get_cpu_frequency() self.power_model = self.create_power_model() # 创建功耗模型 def get_cpu_frequency(self): """尝试获取CPU频率""" try: if platform.system() == 'Windows': # Windows获取CPU频率 import winreg key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"HARDWARE\DESCRIPTION\System\CentralProcessor\0") value, _ = winreg.QueryValueEx(key, "~MHz") winreg.CloseKey(key) return value * 1e6 # MHz转换为Hz else: # Linux/macOS获取CPU频率 with open('/proc/cpuinfo') as f: for line in f: if 'cpu MHz' in line: return float(line.split(':')[1].strip()) * 1e6 # 默认值 return 3.5e9 # 3.5 GHz except: return 3.5e9 # 3.5 GHz def current_time(self): """获取当前CPU周期计数并转换为毫秒""" if platform.system() == 'Windows': return time.perf_counter_ns() / 1_000_000 # 返回毫秒级精确时间 else: # 在实际应用中应使用RDTSC指令 return time.process_time() * 1000 def create_power_model(self): # 简化的线性模型: P = a * utilization + b return lambda u: 15 + 85 * (u/100) # 基础功耗15W + 动态功耗 def estimate_energy(self, physical_time): utilization = self.get_cpu_utilization(physical_time) power = self.power_model(utilization) # 当前功耗(W) time_seconds = physical_time / 1000 # 转换为秒 return power * time_seconds # 能耗(J) class TimerApp: """计时器应用GUI""" def __init__(self, root): self.root = root root.title("高精度计时器 - 专业版") root.geometry("800x600") root.resizable(True, True) # 创建两种计时器实例 self.performance_timer = PerformanceCounterTimer() self.cpu_cycle_timer = CPUCycleTimer() # 当前选中的计时方案 self.current_timer = self.performance_timer self.interrupt_monitor_running = False # 创建UI self.create_widgets() # 启动UI更新循环 self.update_display() # 启动中断监控线程 self.start_interrupt_monitor() def create_widgets(self): """创建界面组件""" # 主框架 main_frame = tk.Frame(self.root) main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) # 左侧控制面板 control_frame = tk.Frame(main_frame) control_frame.pack(side=tk.LEFT, fill=tk.Y, padx=5, pady=5) # 方案选择 mode_frame = tk.LabelFrame(control_frame, text="计时方案", padx=10, pady=10) mode_frame.pack(fill=tk.X, pady=5) self.mode_var = tk.StringVar(value="performance") modes = [("性能计数器 (物理时间)", "performance"), ("CPU周期 (CPU时间)", "cpu")] for text, mode in modes: rb = tk.Radiobutton(mode_frame, text=text, variable=self.mode_var, value=mode, command=self.switch_mode) rb.pack(anchor=tk.W, padx=5, pady=2) # 控制按钮 btn_frame = tk.Frame(control_frame) btn_frame.pack(fill=tk.X, pady=10) self.start_btn = tk.Button(btn_frame, text="开始", width=12, command=self.start_stop) self.start_btn.pack(side=tk.LEFT, padx=5) reset_btn = tk.Button(btn_frame, text="重置", width=12, command=self.reset) reset_btn.pack(side=tk.LEFT, padx=5) # 统计按钮 stats_btn = tk.Button(btn_frame, text="生成报告", width=12, command=self.generate_report) stats_btn.pack(side=tk.LEFT, padx=5) # CPU信息 info_frame = tk.LabelFrame(control_frame, text="系统信息", padx=10, pady=10) info_frame.pack(fill=tk.X, pady=5) cpu_freq = self.cpu_cycle_timer.frequency freq_text = f"CPU频率: {cpu_freq/1e9:.2f} GHz" if cpu_freq >= 1e9 else f"CPU频率: {cpu_freq/1e6:.0f} MHz" self.cpu_label = tk.Label(info_frame, text=freq_text, justify=tk.LEFT) self.cpu_label.pack(anchor=tk.W) cores_text = f"CPU核心数: {os.cpu_count()}" tk.Label(info_frame, text=cores_text, justify=tk.LEFT).pack(anchor=tk.W) # 中断统计 interrupt_frame = tk.LabelFrame(control_frame, text="中断统计", padx=10, pady=10) interrupt_frame.pack(fill=tk.X, pady=5) self.interrupt_label = tk.Label(interrupt_frame, text="中断次数: 0") self.interrupt_label.pack(anchor=tk.W) # 右侧数据显示 display_frame = tk.Frame(main_frame) display_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=5, pady=5) # 双模式时间显示 time_frame = tk.LabelFrame(display_frame, text="时间对比", padx=10, pady=10) time_frame.pack(fill=tk.X, pady=5) # 物理时间显示 physical_frame = tk.Frame(time_frame) physical_frame.pack(fill=tk.X, pady=5) tk.Label(physical_frame, text="物理时间:", width=10).pack(side=tk.LEFT) self.physical_label = tk.Label(physical_frame, text="00:00:00.000", font=("Consolas", 12)) self.physical_label.pack(side=tk.LEFT) # CPU时间显示 cpu_frame = tk.Frame(time_frame) cpu_frame.pack(fill=tk.X, pady=5) tk.Label(cpu_frame, text="CPU时间:", width=10).pack(side=tk.LEFT) self.cpu_time_label = tk.Label(cpu_frame, text="00:00:00.000", font=("Consolas", 12)) self.cpu_time_label.pack(side=tk.LEFT) # 时间分解分析 analysis_frame = tk.LabelFrame(display_frame, text="时间分解分析", padx=10, pady=10) analysis_frame.pack(fill=tk.X, pady=5) # CPU利用率 util_frame = tk.Frame(analysis_frame) util_frame.pack(fill=tk.X, pady=2) tk.Label(util_frame, text="CPU利用率:", width=15).pack(side=tk.LEFT) self.util_label = tk.Label(util_frame, text="0.0%") self.util_label.pack(side=tk.LEFT) # 系统等待时间 wait_frame = tk.Frame(analysis_frame) wait_frame.pack(fill=tk.X, pady=2) tk.Label(wait_frame, text="系统等待时间:", width=15).pack(side=tk.LEFT) self.wait_label = tk.Label(wait_frame, text="0.0 ms") self.wait_label.pack(side=tk.LEFT) # 能耗估算 energy_frame = tk.Frame(analysis_frame) energy_frame.pack(fill=tk.X, pady=2) tk.Label(energy_frame, text="能耗估算:", width=15).pack(side=tk.LEFT) self.energy_label = tk.Label(energy_frame, text="0.0 J") self.energy_label.pack(side=tk.LEFT) # 多核热力图 heatmap_frame = tk.LabelFrame(display_frame, text="多核利用率热力图", padx=10, pady=10) heatmap_frame.pack(fill=tk.BOTH, expand=True, pady=5) # 创建热力图 self.figure, self.ax = plt.subplots(figsize=(6, 3)) self.canvas = FigureCanvasTkAgg(self.figure, heatmap_frame) self.canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True) self.update_heatmap() # 初始化热力图 def switch_mode(self): """切换计时方案""" selected = self.mode_var.get() if selected == "performance": self.current_timer = self.performance_timer else: self.current_timer = self.cpu_cycle_timer # 更新CPU频率显示 cpu_freq = self.cpu_cycle_timer.frequency freq_text = f"CPU频率: {cpu_freq/1e9:.2f} GHz" if cpu_freq >= 1e9 else f"CPU频率: {cpu_freq/1e6:.0f} MHz" self.cpu_label.config(text=freq_text) def start_stop(self): """开始/停止计时""" if self.current_timer.running: self.current_timer.stop() self.start_btn.config(text="开始") else: self.performance_timer.start() self.cpu_cycle_timer.start() self.start_btn.config(text="停止") def reset(self): """重置计时器""" self.performance_timer.reset() self.cpu_cycle_timer.reset() self.physical_label.config(text="00:00:00.000") self.cpu_time_label.config(text="00:00:00.000") self.util_label.config(text="0.0%") self.wait_label.config(text="0.0 ms") self.energy_label.config(text="0.0 J") self.interrupt_label.config(text="中断次数: 0") self.start_btn.config(text="开始") self.update_heatmap() # 重置热力图 def update_heatmap(self): """更新多核热力图""" self.ax.clear() cores = sorted(self.cpu_cycle_timer.core_usage.keys()) if not cores: cores = list(range(os.cpu_count())) util = [0] * len(cores) else: total_time = max(1, self.cpu_cycle_timer.total_elapsed) util = [self.cpu_cycle_timer.core_usage[core] / total_time * 100 for core in cores] # 创建热力图数据 data = np.array(util).reshape(1, -1) im = self.ax.imshow(data, cmap='viridis', aspect='auto', vmin=0, vmax=100) # 添加标注 for i in range(len(cores)): self.ax.text(i, 0, f'{util[i]:.1f}%', ha='center', va='center', color='w') self.ax.set_title(f'CPU核心利用率 (%)') self.ax.set_xticks(range(len(cores))) self.ax.set_xticklabels([f'Core {core}' for core in cores]) self.ax.get_yaxis().set_visible(False) self.figure.colorbar(im, ax=self.ax) self.canvas.draw() def start_interrupt_monitor(self): """启动中断监控线程""" if not self.interrupt_monitor_running: self.interrupt_monitor_running = True threading.Thread(target=self.monitor_interrupts, daemon=True).start() def monitor_interrupts(self): """模拟中断监控""" while self.interrupt_monitor_running: time.sleep(0.1) # 在实际应用中,这里应读取系统中断计数器 if self.current_timer.running: # 模拟随机中断 if np.random.random() < 0.02: # 2%概率发生中断 self.performance_timer.record_interrupt() self.cpu_cycle_timer.record_interrupt() self.root.after(0, self.update_interrupt_count) def update_interrupt_count(self): """更新中断计数显示""" count = self.performance_timer.interrupt_count self.interrupt_label.config(text=f"中断次数: {count}") def generate_report(self): """生成详细分析报告""" import csv import os import platform import sys from datetime import datetime from tkinter import messagebox filename = f"timer_report_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv" with open(filename, 'w', newline='', encoding='utf-8') as f: writer = csv.writer(f) # 写入报告头部信息 writer.writerow(["高精度计时器分析报告"]) writer.writerow(["生成时间", datetime.now().strftime("%Y-%m-%d %H:%M:%S")]) writer.writerow([]) # 写入系统信息 writer.writerow(["系统信息"]) writer.writerow(["操作系统", platform.platform()]) writer.writerow(["CPU核心数", os.cpu_count()]) cpu_freq = self.cpu_cycle_timer.frequency freq_text = f"{cpu_freq/1e9:.2f} GHz" if cpu_freq >= 1e9 else f"{cpu_freq/1e6:.0f} MHz" writer.writerow(["CPU频率", freq_text]) writer.writerow(["Python版本", sys.version]) writer.writerow([]) # 格式化时间函数 def format_ms(ms): hours, rem = divmod(ms, 3600000) minutes, rem = divmod(rem, 60000) seconds, milliseconds = divmod(rem, 1000) return f"{int(hours):02d}:{int(minutes):02d}:{int(seconds):02d}.{int(milliseconds):03d}" # 写入时间指标 physical_time = self.performance_timer.total_elapsed cpu_time = self.cpu_cycle_timer.total_elapsed wait_time = max(0, physical_time - cpu_time) # 系统等待时间 writer.writerow(["时间指标"]) writer.writerow(["度量项", "数值(毫秒)", "格式化时间"]) writer.writerow(["物理时间", f"{physical_time:.3f}", format_ms(physical_time)]) writer.writerow(["CPU时间", f"{cpu_time:.3f}", format_ms(cpu_time)]) writer.writerow(["系统等待时间", f"{wait_time:.3f}", format_ms(wait_time)]) writer.writerow([]) # 写入性能指标 utilization = self.cpu_cycle_timer.get_cpu_utilization(physical_time) if physical_time > 0 else 0 energy = self.cpu_cycle_timer.estimate_energy(physical_time) if physical_time > 0 else 0 writer.writerow(["性能指标"]) writer.writerow(["CPU利用率", f"{utilization:.2f}%"]) writer.writerow(["系统中断次数", self.performance_timer.interrupt_count]) writer.writerow(["估算能耗", f"{energy:.2f} J"]) writer.writerow([]) # 写入多核分析 writer.writerow(["多核利用率分析"]) writer.writerow(["核心ID", "使用时间(毫秒)", "占比"]) # 计算核心使用占比 cores = sorted(self.cpu_cycle_timer.core_usage.keys()) total_cpu_time = max(1, cpu_time) # 防止除零 for core in cores: core_time = self.cpu_cycle_timer.core_usage.get(core, 0) percentage = (core_time / total_cpu_time) * 100 writer.writerow([f"Core {core}", f"{core_time:.3f}", f"{percentage:.2f}%"]) # 添加数学公式注解 writer.writerow([]) writer.writerow(["计算公式注解"]) writer.writerow(["CPU利用率", r"$\eta = \frac{T_{cpu}}{T_{physical}} \times 100\%$"]) writer.writerow(["系统等待时间", r"$\Delta t = T_{physical} - T_{cpu}$"]) writer.writerow(["能耗估算", r"$E = \int_{0}^{T} P(u(t)) \, dt$", "(P=功耗模型)"]) # 显示成功消息 messagebox.showinfo("报告生成", f"分析报告已保存至:\n{os.path.abspath(filename)}") if __name__ == "__main__": root = tk.Tk() app = TimerApp(root) root.mainloop() [Running] python -u "e:\system\Desktop\项目所需文件\工具\计时器工具\timer copy.py" e:\system\Desktop\\u9879�ڏ�������\�H��\\u8ba1\u65f6��H��\timer copy.py:352: UserWarning: Glyph 26680 (\N{CJK UNIFIED IDEOGRAPH-6838}) missing from font(s) DejaVu Sans. self.canvas.draw() e:\system\Desktop\\u9879�ڏ�������\�H��\\u8ba1\u65f6��H��\timer copy.py:352: UserWarning: Glyph 24515 (\N{CJK UNIFIED IDEOGRAPH-5FC3}) missing from font(s) DejaVu Sans. self.canvas.draw() e:\system\Desktop\\u9879�ڏ�������\�H��\\u8ba1\u65f6��H��\timer copy.py:352: UserWarning: Glyph 21033 (\N{CJK UNIFIED IDEOGRAPH-5229}) missing from font(s) DejaVu Sans. self.canvas.draw() e:\system\Desktop\\u9879�ڏ�������\�H��\\u8ba1\u65f6��H��\timer copy.py:352: UserWarning: Glyph 29992 (\N{CJK UNIFIED IDEOGRAPH-7528}) missing from font(s) DejaVu Sans. self.canvas.draw() e:\system\Desktop\\u9879�ڏ�������\�H��\\u8ba1\u65f6��H��\timer copy.py:352: UserWarning: Glyph 29575 (\N{CJK UNIFIED IDEOGRAPH-7387}) missing from font(s) DejaVu Sans. self.canvas.draw() Traceback (most recent call last): File "e:\system\Desktop\\u9879�ڏ�������\�H��\\u8ba1\u65f6��H��\timer copy.py", line 459, in <module> app = TimerApp(root) ^^^^^^^^^^^^^^ File "e:\system\Desktop\\u9879�ڏ�������\�H��\\u8ba1\u65f6��H��\timer copy.py", line 170, in __init__ self.update_display() ^^^^^^^^^^^^^^^^^^^ AttributeError: 'TimerApp' object has no attribute 'update_display'
10-14
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值