Raylib 绘制自定义字体的一种套路

news2024/11/24 19:54:15

Raylib 绘制自定义字体是真的难搞。我的需求是程序可以加载多种自定义字体,英文中文的都有。

我调试了很久成功了!

很有用的参考,建议先看一遍:

瞿华:raylib绘制中文内容
个人笔记|Raylib 的字体使用 - bilibili

再放一下第一篇文章的可用示例代码:

#include <raylib.h>

int main() {
	
	InitWindow(800,600,"世界你好");
	
	Image img=GenImageColor(800,600,WHITE);
	
	//读取字体文件
	unsigned int fileSize;
	unsigned char *fontFileData = LoadFileData("c:\\windows\\fonts\\simhei.ttf", &fileSize);
	
	//ImageDrawCircleEx(&img, 400,300,200,10,BLACK);
	SetTraceLogLevel(LOG_WARNING);
	SetTargetFPS(120);
	while (!WindowShouldClose()) {
		//将要输出的内容放到字符串中(必须是utf8编码)
		char text[]="世界,你好!";
		// 将字符串中的字符逐一转换成Unicode码点,得到码点表
		int codepointsCount;
		int *codepoints=LoadCodepoints(text,&codepointsCount);
		// 读取仅码点表中各字符的字体
		Font font = LoadFontFromMemory(".ttf",fontFileData,fileSize,32,codepoints,codepointsCount);
		// 释放码点表
		UnloadCodepoints(codepoints);
		BeginDrawing();
		ClearBackground(WHITE);
		DrawTextEx(font,text,(Vector2){50,50},32,5,RED);
		EndDrawing();
		//释放字体
		UnloadFont(font);
	}
	UnloadImage(img);
	//释放字体文件内容
	UnloadFileData(fontFileData);
	return 0;
}

(是的,图片img好像没有用)

关键步骤概括为:

  1. LoadFileData 读取字体文件
  2. (while 主循环)
  3. 准备好要输出的文本
  4. LoadCodepoints 用准备的文本加载码点
  5. LoadFontFromMemory 得到含需要输出的文本的字符的字体
  6. UnloadCodepoints 卸载码点
  7. BeginDrawing 开始绘制 使用刚刚的字体绘制文本
  8. EndDrawing 结束绘制
  9. UnloadFont
  10. (循环结束)
  11. UnloadFileData 卸载字体文件

注意每一轮循环都用指定文本的码点加载了新的字体,绘制好后才卸载该字体。
我试图将这一系列操作封装成函数DrawTextPlus,发现UnloadFont必须要在EndDrawing后面执行,不然会输出失败。

下面这张图更离谱了,大错特错!!
盲目封装,一堆错误。输出结果:黑色方块

但是如果在一帧内调用多次BeginDrawing和EndDrawing,还是会出事。。出事代码如下

还是错误的代码,别复制

void DrawTextPlus(const string& s, int x, int y, int fs = 32)
{
 BeginDrawing();

 // 将字符串中的字符逐一转换成Unicode码点,得到码点表
 int codepointsCount;
 int *codepoints=LoadCodepoints(s.c_str(),&codepointsCount);
 // 读取仅码点表中各字符的字体
 Font font = LoadFontFromMemory(".ttf", fontFileData, fileSize, 32, codepoints, codepointsCount);
 // 释放码点表
 UnloadCodepoints(codepoints);
 
 DrawTextEx(font,s.c_str(),(Vector2){x,y},fs,0,RED);
 
 EndDrawing();
 
 //释放字体
 UnloadFont(font);
}

invoke

出现了闪烁现象:
flash

所以一帧还只能调用一次BeginDrawing,EndDrawing。那只能采取其他措施了。
如果真的不封装,需要在同一帧输出不同文本的话,以下代码可以正常运行:

#include <raylib.h>
#include <string>

int main() {
	
	InitWindow(800,600,"世界你好");
	
	//读取字体文件
	unsigned int fileSize;
	unsigned char *fontFileData = LoadFileData("c:\\windows\\fonts\\simhei.ttf", &fileSize);
	
	SetTraceLogLevel(LOG_WARNING);
	SetTargetFPS(120);
	
	//将要输出的内容放到字符串中(必须是utf8编码)s
	size_t scnt = 4;
	const std::string strings[] {"DarkVoxel", "Battle of Phantom", "Poemaze", "TerraSurvivor"};
	
	while (!WindowShouldClose()) 
	{
		
		std::string total_texts{""};
		for (size_t i {0}; i < scnt; ++i)
			total_texts += strings[i];
		
		// 将字符串中的字符逐一转换成Unicode码点,得到码点表
		int codepointsCount;
		int *codepoints=LoadCodepoints(total_texts.c_str(),&codepointsCount);
		
		// 读取仅码点表中各字符的字体
		Font font = LoadFontFromMemory(".ttf",fontFileData,fileSize,32,codepoints,codepointsCount);
		
		// 释放码点表
		UnloadCodepoints(codepoints);
		BeginDrawing();
		ClearBackground(WHITE);
		
		//可以按需要输出了,只要total_texts中有该字符就可以正常输出
		for (size_t i {0}; i < scnt; ++i)
			DrawTextEx(font,strings[i].c_str(),Vector2{50.0f, 50.0f * i}, 32.0f, 5.0f, RED);
		
		EndDrawing();
		//释放字体
		UnloadFont(font);
	}
	//释放字体文件内容
	UnloadFileData(fontFileData);
	return 0;
}

可以发现有好几个地方值得注意以及一点想法:

1.字体整个文件的读取还是在循环前(也就是在程序的载入阶段可以一口气把所有的字体文件读完放进一个容器中)
2.需要输出的文本得提前准备好(如果真的在项目中这样,未免太难受了)
3.在准备码点的时候,可以把需要输出的文本合并在一起(当然可以进行一个字符去重以提高效率)
4.绘制文本的时候只要字符在合并好的文本之中,就可以正常输出
5.每帧都进行了加载和卸载字体的操作(还是变慢了)
6.最后程序退出前卸载时要释放所有的字体文件内容。(释放容器)

小项目就上面这样的写法应该可以接受。但是中大项目就不一样了,动不动就要输出一大堆文本,不可能搞一堆string存在那里,看的都烦;而且每帧都要重新准备字体效率低下。

经过进一步思考,我形成了另一种思路。我在上面的代码中添加了一些【伪代码】:

#include <raylib.h>
#include <string>
#include <map>
#include <vector>

【
容器,存储所有词汇

std::string LSTR(const std::string输出内容ID)
{
        //在我的项目中,支持多语言,我弄一个CSV,专门存储每种语言的词汇,
	//那么这个输出内容ID就可以是中文,方便我阅读代码。
	返回真实的输出内容
}int main() 
{
	
	InitWindow(800,600,"世界你不好");
	
	//读取
	

         读取你的CSV文件并存储到一个容器中,以供上面的LSTR函数使用

	map<std::string, pair <std::string, unsigned char*>> 所有需要用到的字体名称以及路径、数据;
	{
		{..., {..., nullptr}},
		{..., {..., nullptr}},
	};
	
	】
	
	【
		for (const auto& fdt : ...)
		{
			unsigned int fileSize;
			unsigned char *fontFileData = LoadFileData(字体文件路径, &fileSize);
			
			把fontFileData存进去
			
			string 整合串= 去重后的把CSV文件所有内容拼接在一起的字符串;
			
			// 将字符串中的字符逐一转换成Unicode码点,得到码点表
			int codepointsCount;
			int *codepoints=LoadCodepoints(整合串.c_str(), &codepointsCount);
			
			// 读取仅码点表中各字符的字体
			Font font = LoadFontFromMemory(取字体路径扩展名, 字体文件内容fontFileData,fileSize, 200, codepoints, codepointsCount);
			
                        把字体装进去

			// 释放码点表
			UnloadCodepoints(codepoints);
		}SetTraceLogLevel(LOG_WARNING);
	SetTargetFPS(120);
	
	//将要输出的内容放到字符串中(必须是utf8编码)
	
	while (!WindowShouldClose()) 
	{
		BeginDrawing();
		ClearBackground(WHITE);
		
		//可以按需要输出了,只要total_texts中有该字符就可以正常输出
//CUR_FONT 是一个宏,获取当前字体
		DrawTextEx(CUR_FONT,LSTR("CSV中"),Vector2{50.0f, 50.0f}, 80.0f, 5.0f, BLACK);
		DrawTextEx(CUR_FONT,LSTR("包含的内容"),Vector2{50.0f, 130.0f}, 80.0f, 5.0f, BLACK);
		DrawTextEx(CUR_FONT,LSTR("都可以写"),Vector2{50.0f, 210.0f}, 80.0f, 5.0f, BLACK);
		
		EndDrawing();
	}for (auto& fdt : ...)
		{
			UnloadFileData(字体文件内容指针);
		}return 0;
}

注意你需要准备一个文件(例如CSV格式的),每行存储一个你需要的字符串,然后LSTR函数的参数就是你访问任意一个字符串的索引(可以是数字【我觉得挺烦的,还要查】,可以是字符串【本身】)。正如我注释中写的,我的程序支持多语言,因此可以每行一个中文,逗号,一个英文,然后用中文索引,特别方便。

这样的结构虽然很难搞,但是大大简化了中间绘制文本的代码,只需要加个LSTR这样的函数即可,无需手动准备一堆string来搞临时的字体再输出。
如果你不想撰写新的文件存储所要用的字符串,还有几种偷懒的方法(仅供参考):

(1)写一个辅助的程序,在要编译前运行它,提取你的源文件中的字符串然后整合在一起,再把字符串写进去然后编译(雾)。
(2)把所有字符(例如汉字)加载进去(日常试试可以,实际运用肯定不现实,内存都要爆了)

上面的伪代码可能看的不是很明白,我也不可能全部帮你补全,只能提供一些我跑成功的项目的代码或是截图,希望对你有帮助:

语言、词汇处理

enum LangID
{
	Chinese = 0,
	English = 1,
};
#define LANG_CNT 2

//下标宏
#define LID_LANG 0	//各语言名称
#define LID_GAME_NAME 1

vector<vector<string>> lang_words;

bool ReadLanguage();

constexpr const char* PunctAndNumberString(void)
{
	return "0123456789,.?/<>()~`[]{}\\|\"\':;!@#$%^&*-=_+ ";
}
constexpr const char* PunctAndNumberStringIncludingChinese(void)
{
	return " 0123456789,.?/<>()~`[]{}\\|\"\':;!@#$%^&*-=_+,。?!、()【】“”‘’;:《》·…—";
}
string ObtainNormalEnglish(const string& s)
{
	string res;

	bool wordbeg{ true };
	for (char ch : s)
	{
		if (wordbeg && isalpha(ch))
		{
			res += islower(ch) ? toupper(ch) : ch;
			wordbeg = false;
		}
		else if (isalpha(ch))
		{
			res += ch;
		}
		else if (ch == '_' || ch == ' ')
		{
			res += ' ';
			wordbeg = true;
		}
	}
	return res;
}
string AssembleTotalChineseString(void);
string AssembleTotalEnglishString(void)
{
	string res;
	for (char ch = 'A'; ch <= 'Z'; ++ch)
		res += str(ch);
	for (char ch = 'a'; ch <= 'z'; ++ch)
		res += str(ch);
	res += PunctAndNumberString();
	return res;
}
string UniqueChinese(const string& s) {
	string result;
	unordered_set<int> chineseChars;
	
	for (size_t i = 0; i < s.length(); i++) {
		// 检查当前字符是否是中文字符
		if ((s[i] & 0xE0) == 0xE0) {
			int codePoint = ((s[i] & 0x0F) << 12) | ((s[i + 1] & 0x3F) << 6) | (s[i + 2] & 0x3F);
			
			// 如果当前中文字符不在哈希集合中,则将其添加到结果字符串和哈希集合中
			if (chineseChars.find(codePoint) == chineseChars.end()) {
				result += s.substr(i, 3);
				chineseChars.insert(codePoint);
			}
			
			// 由于中文字符占用3个字节,因此增加索引i的值
			i += 2;
		}
		else {
			result += s[i];
		}
	}
	
	return result;
}

bool ReadLanguage()
{
	string path = g.data_dir + "Language.csv";
	if (!ExistFile(path))
	{
		ErrorLogTip(nullptr, "Cannot Find the Language File :(\n" + path, "ReadLanguage");
		return false;
	}
	DebugLog("读取语言...");
	vector<string> lines = ReadFileLines(path);
	int i{ 1 };
	while (i < lines.size())
	{
		string line = lines.at(i);
		if (line.empty())
		{
			++i;
			continue;
		}

		line = strrpc(line, " ", "$");
		line = strrpc(line, ",", " ");
		
		stringstream ss;
		string tmp;
		
		ss << line;
		
		vector<string> langs;
		for (int i = 0; i < LANG_CNT; ++i)
		{
			ss >> tmp;
			tmp = strrpc(tmp, "$", " ");
			tmp = strrpc(tmp, "^", ",");
			langs.push_back(tmp);
		}
		// DebugLog(str(langs));
		lang_words.push_back(langs);
		++i;
	}
	
	for (const auto& idt : itemdata)
		lang_words.push_back(vector{ idt.cn_name, ObtainNormalEnglish(idt.en_name) });
	for (const auto& edt : entitydata)
		lang_words.push_back(vector{ edt.cn_name, ObtainNormalEnglish(edt.en_name) });
	for (const auto& bdt : buffdata)
		lang_words.push_back(vector{ bdt.cn_name, ObtainNormalEnglish(bdt.en_name) });
	for (const auto& pdt : placeabledata)
		lang_words.push_back(vector{ pdt.cn_name, ObtainNormalEnglish(pdt.en_name) });
	for (const auto& rdt : random_tips)
		lang_words.push_back(rdt.versions);

	DebugLog("共计", lang_words.size(), "个词汇,支持", LANG_CNT, "门语言");
	return true;
}
string AssembleTotalChineseString(void)
{
	string res;
	
	//英文也要
	for (char ch = 'A'; ch <= 'Z'; ++ch)
		res += str(ch);
	for (char ch = 'a'; ch <= 'z'; ++ch)
		res += str(ch);
	
	//然后是中文
	for (const auto& pr : lang_words)
		res += pr.at(Chinese);

	res += PunctAndNumberStringIncludingChinese();
	
	res = UniqueChinese(res);
	
	
	return res;
}
#define CHN_FONTNAME "Sthginkra Italic"

map<LangID, string> lang_font_names
{
    {Chinese, CHN_FONTNAME},
    {English, "Andy Bold"},
};

#define CUR_FONTNAME (g.lang_font_names[g.lang].c_str())
#define CENTER_TITLE_CHN_FONTNAME "钉钉进步体"

map<string, pair<string, LangID>> used_fonts
{
    {"Andy Bold", {"ANDYB.TTF", English}},
    {CHN_FONTNAME, {"ZhouFangRiMingTiXieTi-2.otf", Chinese}}, //我不是舟批
    {CENTER_TITLE_CHN_FONTNAME, {"DingTalk JinBuTi.ttf", Chinese}},
};


DebugLog("安装", used_fonts.size() - 1, "个字体...");

unsigned char* pFileData{ nullptr };

auto iter = used_fonts.begin();
for (;iter != used_fonts.end(); ++iter)
{
	if (iter->second.second == English)
		continue;
	
	auto pr = make_pair(iter->first,
		make_pair(ProduceMemoryFont(iter->second.first, iter->second.second, &pFileData),
			pFileData)); //见下文
	cout << iter->second.first << "  " << iter->second.second  << "  " << pr.first << "  " << pr.second.first.glyphCount << '\n';
	g.fonts.insert(pr);
}

DebugLog("加载 " + str(g.fonts.size()) + " 个字体完毕");
Font ProduceMemoryFont(const string& filename, LangID lid, unsigned char** pFileData)
{
	string s;
	switch (lid)
	{
	case Chinese:
		s = AssembleTotalChineseString();
		break;
	case English:
		s = AssembleTotalEnglishString();
		break;
	default:
		return GetFontDefault();
	}
		
	Font font;
	unsigned int fileSize{ 0U };
	
	unsigned char* fontFileData = LoadFileData((g.font_dir + filename).c_str(), &fileSize);
	*pFileData = fontFileData;
	if (fontFileData == nullptr)
	{
		DebugLog("ERROR: fontFileData is empty");
	}
	int codepointsCount;
	cout << "LoadCodepoints...\n";
	cout << "s=" << s << '\n';
	int* codepoints = LoadCodepoints(s.c_str(), &codepointsCount);
	if (!codepoints)
	{
		cout << "ERROR: LoadCodePoints failed\n";
	}
	cout << "CodepointsCount=" << codepointsCount << '\n';
	cout << "FileSize=" << fileSize << '\n';
	string ext = GetFileExtension(filename.c_str());
	cout << "Ext=" << ext << '\n';
	// 读取仅码点表中各字符的字体
	cout << "LoadFontFromMemory...\n";
	
	font = LoadFontFromMemory(ext.c_str(), fontFileData,
		fileSize, 200, codepoints, codepointsCount);	//200挺合适的
	// 释放码点表
	cout << "UnloadCodepoints...\n";
	UnloadCodepoints(codepoints);
	
	return font;
}
DebugLog("卸载", used_fonts.size(), "个字体...");
for (const auto& fn : used_fonts)
{
    UnloadFont(g.fonts[fn.first].first);
    UnloadFileData(g.fonts[fn.first].second);
}

控制台输出截图(非中文字符去重我好像没做):
L

怎么样,有思路了吗?
大概就是把要输出的字符串提前收集好,然后装载字体一次就行,后面就随心所欲输出就行了。

还有几点:
1.装载字体时的字号选 200 是挺合适的值,如果太低就马赛克了,太高会出问题
2.CSV文件可能是这样的:
软件名称:Modern CSV

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1689832.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Nginx - 健康检查终极指南:探索Upstream Check模块

文章目录 概述upstream_check_module模块安装和配置指南模块安装步骤基本配置示例详细配置说明检查类型和参数常见问题及解决方案 SSL检查和DNS解析功能SSL检查配置示例和说明配置示例 DNS解析配置示例和说明配置示例 结合实际应用场景的高级配置示例综合SSL检查与DNS解析 总结…

代码随想录算法训练营第三天| 203.移除链表元素、 707.设计链表、 206.反转链表

203.移除链表元素 题目链接&#xff1a; 203.移除链表元素 文档讲解&#xff1a;代码随想录 状态&#xff1a;没做出来&#xff0c;做题的时候定义了一个cur指针跳过了目标val遍历了一遍链表&#xff0c;实际上并没有删除该删的节点。 错误代码&#xff1a; public ListNode re…

一键恢复安卓手机数据:3个快速简便的解决方案!

安卓手机作为我们不可或缺的数字伙伴&#xff0c;承载着大量珍贵的个人和工作数据。然而&#xff0c;随着我们在手机上进行各种操作&#xff0c;不可避免地会遇到一些令人头痛的问题&#xff0c;比如意外删除文件、系统故障或其他不可预见的情况&#xff0c;导致重要数据的丢失…

springboot基于Web前端技术的java养老院管理系统_utbl7

3.普通用户模块包括&#xff1a;普通会员的注册、养老院客房查询、养老院留言查询、预约老人基本信息登记、选择房间、用户缴费的功能。 4.数据信息能够及时进行动态更新&#xff0c;增删&#xff0c;用户搜素方便&#xff0c;使用户可以直接浏览相关信息&#xff0c;要考虑便于…

埋点——about前端

所谓“埋点”&#xff0c;是数据采集领域(尤其是用户行为数据采集领域)的术语&#xff0c;指的是针对特定用户行为或事件进行捕获、处理和发送的相关技术及其实施过程。比如用户某个icon点击次数、观看某个视频的时长等等,埋点的技术实质&#xff0c;是先监听软件应用运行过程中…

C#数据类型变量、常量

一个变量只不过是一个供程序操作的存储区的名字。 在 C# 中&#xff0c;变量是用于存储和表示数据的标识符&#xff0c;在声明变量时&#xff0c;您需要指定变量的类型&#xff0c;并且可以选择性地为其分配一个初始值。 在 C# 中&#xff0c;每个变量都有一个特定的类型&…

只需提交几次代码就能轻松实现系统级的变更!——“基础设施即代码”模式与实践

“基础设施即代码”模式与实践 基础设施即代码&#xff08;Infrastructure as Code&#xff0c;IaC&#xff09;是指利用脚本、配置或编程语言创建和维护基础设施的一组实践和流程。通过IaC&#xff0c;我们可以轻松测试各个组件、实现所需的功能并在最小化停机时间的前提下进行…

51-指针_野指针,指针运算

51-1 野指针 51-1-1 什么是野指针 概念&#xff1a;野指针就是指针指向的位置是不可知的&#xff08;随机的、不正确的、没有明确限制的) 没有初始化 int main() {int* p;//p没有初始化&#xff0c;就意味着没有明确的指向//一个局部变量不初始化的话&#xff0c;放的是随机…

一文说透组织中的利润中心建设

当前&#xff0c;规模型企业越来越重视利润中心建设&#xff0c;华为的实践表明&#xff0c;建好了利润中心&#xff0c;利润自然好&#xff0c;组织也会上台阶。很多企业利润上不去&#xff0c;就是没有搞好利润中心这个火车头。然而&#xff0c;很多人误以为利润中心只是财务…

Cesium与Three相机同步(2)

之前实现了将Three相机同步到Cesium相机Cesium与Three相机同步(1)-CSDN博客 现在是将Cesium相机同步到Three相机,从而实现了相机双向同步。 <!DOCTYPE html> <html lang="en"><head><title>three.js webgl - orbit controls</title&g…

python:pycharm虚拟解释器报错环境位置目录为空

目录 解释器分控制台解释器 和 pycharm解释器 控制台解释器切换&#xff1a; pycharm解释器 解释器分控制台解释器 和 pycharm解释器 控制台解释器切换&#xff1a; 切换到解释器下 激活解释器 查看解释器 where python 激活成功 这时在控制台使用python xxx.py 可以…

OpenCV:入门(五)

图像梯度 图像梯度计算的是图像变化的速度。对于图像的边缘部分&#xff0c;其灰度值变化较大&#xff0c;梯度值也 较大&#xff1b;相反&#xff0c;对于图像中比较平滑的部分&#xff0c;其灰度值变化较小&#xff0c;相应的梯度值也较小。一般情 况下&#xff0c;图像梯度计…

k8s 1.28.10 浏览器访问6443查看api,需要证书

添加证书 使用client-certificate-data和client-key-data生成一个p12文件 1.生成client-certificate-data grep client-certificate-data ~/.kube/config | head -n 1 | awk {print $2} | base64 -d >> kubecfg.crt2.生成client-key-data grep client-key-data ~/.kub…

深 渔博会成功举办 新文件促进城市数字化转型| 产业互联网观察174期

深圳渔博会水产品竞价交易会成功举办 首批数据跨境一般数据清单发布 新文件加快城市全域数字化转型进程 | 产业互联网观察第174期 2024深圳渔博会水产品竞价交易会成功举办 5月17日至18日&#xff0c;深圳渔博会上深圳国际金枪鱼交易有限公司在深圳会展中心成功举办了水产品竞…

使用Prometheus + Blackbox-exporter快速监控一个网站性能和SSL过期时间

使用blackbox-exporter快速监控一个网站性能和SSL过期时间 环境介绍什么是blackbox-exporter下载blackbox-exporter安装blackbox-exporter配置Prometheus服务端查看job上线监控面板参考文献 环境介绍 本文实验环境 操作系统&#xff1a;Centos 7.9Prometheus版本&#xff1a;…

U-Mail邮件系统为用户提供更加安全的数据保护机制

据外媒报道&#xff0c;近日美国国家安全委员会泄露了其成员的近1万封电子邮件和密码&#xff0c;暴露了政府组织和大公司在内的2000家公司。其中包括美国国家航空航天局和特斯拉等。报道称该漏洞于3月7日被研究人员发现&#xff0c;通过该漏洞攻击者能够访问对web服务器操作至…

哪款电脑桌面日历记事本软件好用?推荐优秀的电脑日历记事本

对于众多上班族而言&#xff0c;每天在电脑前忙碌已成为生活常态。若想提升工作效率&#xff0c;简化繁琐的工作流程&#xff0c;选择一款出色的电脑桌面日历与记事本软件就显得至关重要。 然而&#xff0c;在Windows操作系统上设定提醒显得相当繁琐&#xff0c;而系统自带的记…

创新力作 焕新首发丨捷顺科技·捷曜系列智慧停车新品全新上市

2024捷顺科技智慧停车全家族新品全面上市 全新外观、全新特性、全新体验 新控制机、新道闸、新超眸相机... 每款新品都有哪些功能亮点 带您一探究竟

影响程序员发展,首个关于“软件供应链安全”国家标准发布,你该知道的10个问题!【附标准全文】

近日&#xff0c;GB/T 43698-2024《网络安全技术 软件供应链安全要求》作为国内首个软件供应链安全的国标&#xff0c;对于程序员的影响深远。该标准的实施&#xff0c;不仅为程序员提供了明确的软件安全开发指导&#xff0c;还强化了他们在软件开发过程中对安全性的重视。程序…

如何解决Nginx反向代理不生效?

目录 背景 过程 日志 检查配置文件 重启服务 检查容器内的配置文件 容器和宿主机 其他 背景 用了两年的nginx新加的反向代理不生效 Docker挂载的配置文件启动的Nginx&#xff0c;配置一切正常&#xff0c;但是反向代理不生效&#xff0c;???先自查一波 过程 日志 …