Word 转成pdf及打印的开源方案支持xp

Word转成pdf、打印的方案几乎没有免费开源的方案,现在提供一个通过LibreOffice实现的方案

操作依赖LibreOffice需要安装,点此下载老版本

5.4.7.2是最后一个支持xp的 版本如需xp要请安装此版本

LibreOffice官方介绍

LibreOffice 是一款开放源代码的自由免费全能办公软件,可运行于 Microsoft Windows, GNU/Linux 以及 macOS 等操作系统上。它包含了 Writer, Calc, Impress, Draw, Math 以及 Base 等组件,可分别用于文本文档、电子表格、幻灯片演示文稿、绘图文档、数学公式编辑、数据库管理等工作。

LibreOffice 采用对企业和个人用户均免费的 MPL 2.0 授权协议。您可以自由分发该软件,无需支付授权费用(但您仍然可以付费获得经认证的专业支持)。它的源代码完全公开,任何人都可以参与软件的开发和维护。

一、通过进程的方式

1.Word打印

  public void PrintWordFile(string file, string printerName){if (string.IsNullOrEmpty(file)) throw new Exception("Invalid parameters passed to convert word function.");if (!File.Exists(file)) throw new FileNotFoundException($"The file passed to the convert word process ({file}) could not be found.");var fileInfo = new FileInfo(file);if (fileInfo.Extension.ToLower() != ".doc" && fileInfo.Extension.ToLower() != ".docx")throw new ArgumentOutOfRangeException($"The file type passed to the convert word process is an invalid type ({fileInfo.Extension}).");var libreOfficePath = Path.Combine(LibreOfficePath, "swriter.exe");if (!File.Exists(libreOfficePath)) throw new FileNotFoundException("It seems that LibreOffice is not where it should be, please ensure the path exists.");var procStartInfo = new ProcessStartInfo(libreOfficePath, $@"--headless --pt ""{printerName}"" ""{file}"""){RedirectStandardOutput = true,RedirectStandardError = true,UseShellExecute = false,CreateNoWindow = true,WorkingDirectory = Environment.CurrentDirectory};Process process = new Process() { StartInfo = procStartInfo };// Attach event handlers to capture output and error streamsprocess.OutputDataReceived += (sender, args) => Console.WriteLine("OUT: " + args.Data);process.ErrorDataReceived += (sender, args) => Console.WriteLine("ERR: " + args.Data);process.Start();// Start reading the output asynchronouslyprocess.BeginOutputReadLine();process.BeginErrorReadLine();process.WaitForExit();if (process.ExitCode != 0){throw new LibreOfficeFailedException(process.ExitCode);}}

2.Word转成PDF

  public string ConvertWordFile(string file, string outputDirectory){if (string.IsNullOrEmpty(file) || string.IsNullOrEmpty(outputDirectory)) throw new Exception("Invalid parameters passed to convert word function.");if (!File.Exists(file)) throw new FileNotFoundException($"The file passed to the convert word process ({file}) could not be found.");if (!Directory.Exists(outputDirectory)) throw new DirectoryNotFoundException($"The output folder passed to the convert word process ({outputDirectory}) does not exist.");if (outputDirectory.EndsWith(@"\")) outputDirectory = outputDirectory.TrimEnd('\\');var fileInfo = new FileInfo(file);if (fileInfo.Extension.ToLower() == ".doc" && fileInfo.Extension.ToLower() == ".docx") throw new ArgumentOutOfRangeException($"The file type passed to the convert word process is an invalid type ({fileInfo.Extension}).");var outputFile = outputDirectory + @"\" + Path.GetFileNameWithoutExtension(fileInfo.Name) + ".pdf";if (File.Exists(outputFile)) File.Delete(outputFile);var libreOfficePath = Path.Combine(LibreOfficePath, "swriter.exe");if (!File.Exists(libreOfficePath)) throw new FileNotFoundException("It seems that LibreOffice is not where it should be, please ensure the path exists.");var procStartInfo = new ProcessStartInfo(libreOfficePath, $@"--headless --convert-to pdf:writer_pdf_Export ""{file}"" --outdir ""{outputDirectory}"""){RedirectStandardOutput = true,UseShellExecute = false,CreateNoWindow = true,WorkingDirectory = Environment.CurrentDirectory};Process process = new Process() { StartInfo = procStartInfo };process.Start();process.WaitForExit();if (process.ExitCode != 0)throw new LibreOfficeFailedException(process.ExitCode);if (!File.Exists(outputFile)) throw new FileNotFoundException("The convert to word process has failed to convert the file!");return outputFile;}public class LibreOfficeFailedException : Exception{public LibreOfficeFailedException(int exitCode) : base($"LibreOffice has failed with {exitCode}") { }}

二、通过cli库调用,下附代码

CLI下载

   public class WordPrint{private XComponentContext context;private XMultiServiceFactory service;private XComponentLoader component;private XComponent doc;private static WordPrint wordPrint;private List<string> filters = new List<string>();#region ConstructorsWordPrint(){NativeMethods.InitializationLibrary();/// This will start a new instance of OpenOffice.org if it is not running, /// or it will obtain an existing instance if it is already open.context = uno.util.Bootstrap.bootstrap();/// The next step is to create a new OpenOffice.org service managerservice = (XMultiServiceFactory)context.getServiceManager();/// Create a new Desktop instance using our service managercomponent = (XComponentLoader)service.createInstance("com.sun.star.frame.Desktop");// Getting filtersXNameContainer filters = (XNameContainer)service.createInstance("com.sun.star.document.FilterFactory");foreach (string filter in filters.getElementNames())this.filters.Add(filter);}~MiniWordPrint(){if (doc != null)doc.dispose();doc = null;}#endregion#region Private methodsprivate string FilterToString(ExportFilter filter){switch (filter){case ExportFilter.Word97: return "MS Word 97";case ExportFilter.WriterPDF: return "writer_pdf_Export";case ExportFilter.CalcPDF: return "calc_pdf_Export";case ExportFilter.DrawPDF: return "draw_pdf_Export";case ExportFilter.ImpressPDF: return "impress_pdf_Export";case ExportFilter.MathPDF: return "math_pdf_Export";}return "";}#endregion#region Public methods/// <summary>/// load docx/// </summary>/// <param name="filename">file path</param>/// <param name="hidden">load document invisible Defines if the loaded component is made visible. If this property is not specified, the component is made visible by default.</param>/// <returns></returns>public bool Load(string filename, bool hidden){return Load(filename, hidden, "", "");}/// <summary>/// load docx/// </summary>/// <param name="filename">file path</param>/// <param name="hidden">load document invisible Defines if the loaded component is made visible. If this property is not specified, the component is made visible by default. </param>/// <param name="filter_index">internal filter name <see cref="Filters"/> Filters index/// Name of a filter that should be used for loading or storing the component.Names must match the names of the TypeDetection configuration, invalid names are ignored.If a name is specified on loading, it still will be verified by a filter detection, but in case of doubt it will be preferred.</param>/// <param name="filter_options">additional properties for filter/// Some filters need additional parameters; use only together with property MediaDescriptor::FilterName.Details must be documented by the filter. This is an old format for some filters. If a string is not enough, filters can use the property MediaDescriptor::FilterData.</param>/// <returns></returns>public bool Load(string filename, bool hidden, int filter_index, string filter_options){return Load(filename, hidden, filters[filter_index], filter_options);}/// <summary>/// load docx/// </summary>/// <param name="filename">file path</param>/// <param name="hidden">load document invisible Defines if the loaded component is made visible. If this property is not specified, the component is made visible by default.</param>/// <param name="filter_name">internal filter name <see cref="Filters"/>/// Name of a filter that should be used for loading or storing the component.Names must match the names of the TypeDetection configuration, invalid names are ignored.If a name is specified on loading, it still will be verified by a filter detection, but in case of doubt it will be preferred.</param>/// <param name="filter_options"> additional properties for filter/// Some filters need additional parameters; use only together with property MediaDescriptor::FilterName.Details must be documented by the filter. This is an old format for some filters. If a string is not enough, filters can use the property MediaDescriptor::FilterData.</param>/// <returns></returns>public bool Load(string filename, bool hidden, string filter_name, string filter_options){List<PropertyValue> pv = new List<PropertyValue>();pv.Add(new PropertyValue("Hidden", 0, new uno.Any(hidden), PropertyState.DIRECT_VALUE));if (filter_name != ""){pv.Add(new PropertyValue("FilterName", 0, new uno.Any(filter_name), PropertyState.DIRECT_VALUE));pv.Add(new PropertyValue("FilterOptions", 0, new uno.Any(filter_options), PropertyState.DIRECT_VALUE));}try{doc = component.loadComponentFromURL("file:///" + filename.Replace('\\', '/'), "_blank",0, pv.ToArray());return true;}catch{doc = null;return false;}}/// <summary>///  a given document xDoc to print to the standard printer without any settings/// </summary>/// <returns></returns>public bool Print(){return Print("", 1, "");}/// <summary>/// a given document xDoc to print /// </summary>/// <param name="printName">string - Specifies the name of the printer queue to be used.</param>/// <param name="copies">short - Specifies the number of copies to print.</param>/// <param name="pages">string - Specifies the pages to print in the same format as in the print dialog of the GUI (e.g. "1, 3, 4-7, 9-")</param>/// <param name="orientation">com.sun.star.view.PaperOrientation. Specifies the orientation of the paper.</param>/// <param name="paperFormat">com.sun.star.view.PaperFormat. Specifies a predefined paper size or if the paper size is a user-defined size.</param>/// <returns></returns>public bool Print(string printName, int copies, string pages, MiniOrientation orientation = MiniOrientation.PORTRAIT, MiniFormat paperFormat = MiniFormat.A4){var printerDesc = new List<PropertyValue>();if (!string.IsNullOrEmpty(printName))printerDesc.Add(new PropertyValue("Name", 0, new uno.Any(typeof(string), printName), PropertyState.DIRECT_VALUE));printerDesc.Add(new PropertyValue("PaperOrientation", 0, new uno.Any(typeof(PaperOrientation), orientation), PropertyState.DIRECT_VALUE));printerDesc.Add(new PropertyValue("PaperFormat", 0, new uno.Any(typeof(PaperFormat), paperFormat), PropertyState.DIRECT_VALUE));var printOpts = new List<PropertyValue>();printOpts.Add(new PropertyValue("CopyCount", 0, new uno.Any(copies), PropertyState.DIRECT_VALUE));if (!string.IsNullOrEmpty(pages))printOpts.Add(new PropertyValue("Pages", 0, new uno.Any(pages), PropertyState.DIRECT_VALUE));printOpts.Add(new PropertyValue("Wait", 0, new uno.Any(true), PropertyState.DIRECT_VALUE));//if (doc is XPrintable)try{((XPrintable)doc).setPrinter(printerDesc.ToArray());((XPrintable)doc).print(printOpts.ToArray());return true;}catch { return false; }}/// <summary>/// a given document xDoc to print with custom/// </summary>/// <param name="printerDesc">///----------- Properties of com.sun.star.view.PrinterDescriptor--------///*** Name string - Specifies the name of the printer queue to be used.///*** PaperOrientation com.sun.star.view.PaperOrientation.Specifies the orientation of the paper.///*** PaperFormat com.sun.star.view.PaperFormat.Specifies a predefined paper size or if the paper size is a user-defined size.///*** PaperSize com.sun.star.awt.Size.Specifies the size of the paper in 1/100 mm.///*** IsBusy boolean - Indicates if the printer is busy.///*** CanSetPaperOrientation boolean - Indicates if the printer allows changes to.PaperOrientation///*** CanSetPaperFormat boolean - Indicates if the printer allows changes to.PaperFormat///*** CanSetPaperSize boolean - Indicates if the printer allows changes to.PaperSize/// </param>/// <param name="printerDesc">///------------- Properties of com.sun.star.view.PrintOptions--------///CopyCount short - Specifies the number of copies to print.///FileName string - Specifies the name of a file to print to, if set.///Collate boolean - Advises the printer to collate the pages of the copies.If true, a whole document is printed prior to the next copy, otherwise the page copies are completed together.///Pages string - Specifies the pages to print in the same format as in the print dialog of the GUI (e.g. "1, 3, 4-7, 9-")///Wait boolean - Advises that the print job should be performed synchronously, i.e.wait until printing is complete before returning from printing.Otherwise return is immediate and following actions(e.g.closing the corresponding model) may fail until printing is complete.Default is false./// </param>/// <param name="pagePrintSettings">/// ------------- Properties of com.sun.star.text.PagePrintSettings--------/// PageRows short - Number of rows in which document pages should appear on the output page./// PageColumns short - Number of columns in which document pages should appear on the output page./// LeftMargin long - Left margin on the output page./// RightMargin long - Right margin on the output page./// TopMargin long - Top margin on the output page./// BottomMargin long - Bottom margin on the output page./// HoriMargin long - Margin between the columns on the output page./// VertMargin long - Margin between the rows on the output page./// IsLandscape boolean - Determines if the output page is in landscape format./// </param>/// <returns></returns>public bool Print(List<MiniPropertyValue> printerDesc, List<MiniPropertyValue> printOpts, List<MiniPropertyValue> pagePrintSettings){try{var printSettings = pagePrintSettings.ConvertAll(v => ToPropertyValue(v));var desc = printerDesc.ConvertAll(v => ToPropertyValue(v));var opts = printOpts.ConvertAll(v => ToPropertyValue(v));((XPagePrintable)doc).setPagePrintSettings(printSettings.ToArray());((XPrintable)doc).setPrinter(desc.ToArray());((XPrintable)doc).print(opts.ToArray());return true;}catch { return false; }}/// <summary>/// save pdf/// </summary>/// <param name="filename">file path</param>/// <param name="filter"><see cref="ExportFilter"/></param>/// <returns></returns>public bool Save(string filename, ExportFilter filter){return Save(filename, FilterToString(filter));}/// <summary>/// save pdf/// </summary>/// <param name="filename">file path</param>/// <param name="filter">/// internal filter name <see cref="Filters"/>/// Name of a filter that should be used for loading or storing the component.Names must match the names of the TypeDetection configuration, invalid names are ignored.If a name is specified on loading, it still will be verified by a filter detection, but in case of doubt it will be preferred./// </param>/// <returns></returns>public bool Save(string filename, string filter){List<PropertyValue> pv = new List<PropertyValue>();pv.Add(new PropertyValue("FilterName", 0, new uno.Any(filter), PropertyState.DIRECT_VALUE));pv.Add(new PropertyValue("Overwrite", 0, new uno.Any(true), PropertyState.DIRECT_VALUE));try{filename = filename.Replace("\\", "/");((XStorable)doc).storeToURL("file:///" + filename, pv.ToArray());return true;}catch { return false; }}/// <summary>/// export pdf/// </summary>/// <param name="filename">file path</param>/// <returns></returns>public bool ExportToPdf(string filename){filename = Path.ChangeExtension(filename, ".pdf");bool ret = Save(filename, "writer_pdf_Export");if (!ret) ret = Save(filename, "impress_pdf_Export");if (!ret) ret = Save(filename, "calc_pdf_Export");if (!ret) ret = Save(filename, "draw_pdf_Export");if (!ret) ret = Save(filename, "impress_pdf_Export");if (!ret) ret = Save(filename, "math_pdf_Export");return ret;}/// <summary>/// close XComponent /// doc stream must be not use,otherwise dispose() has no return/// </summary>public void Close(){doc.dispose();doc = null;}/// <summary>///  new docx/// </summary>/// <param name="app"><see cref="AppType"/></param>/// <param name="hidden">load document invisible Defines if the loaded component is made visible. If this property is not specified, the component is made visible by default.</param>/// <returns></returns>public bool New(AppType app, bool hidden){try{string sapp = "private:factory/";switch (app){case AppType.Writer:sapp += "swriter";break;case AppType.Calc:sapp += "scalc";break;case AppType.Impress:sapp += "simpress";break;case AppType.Draw:sapp += "sdraw";break;case AppType.Math:sapp += "smath";break;}PropertyValue pv = new PropertyValue("Hidden", 0, new uno.Any(hidden), PropertyState.DIRECT_VALUE);doc = component.loadComponentFromURL(sapp, "_blank", 0, new PropertyValue[1] { pv });return true;}catch{doc = null;return false;}}#endregion#region Properties/// <summary>/// internal filter name/// Name of a filter that should be used for loading or storing the component.Names must match the names of the TypeDetection configuration, invalid names are ignored.If a name is specified on loading, it still will be verified by a filter detection, but in case of doubt it will be preferred./// </summary>public List<string> Filters{get { return filters; }}#endregionprivate PropertyValue ToPropertyValue(MiniPropertyValue miniProperty){return new PropertyValue(Name: miniProperty.Name,Handle: miniProperty.Handle,Value: new uno.Any(type: miniProperty.Value.Type,value: miniProperty.Value.Value),State: (PropertyState)miniProperty.State);}}

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

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

相关文章

蓝桥杯备考:数据结构之栈 和 stack

目录 栈的概念以及栈的实现 STL 的stack 栈和stack的算法题 栈的模板题 栈的算法题之有效的括号 验证栈序列 后缀表达式 括号匹配 栈的概念以及栈的实现 栈是一种只允许在一端进行插入和删除的线性表 空栈&#xff1a;没有任何元素 入栈&#xff1a;插入元素消息 出…

C++中的条件变量(condition_variable)详解:小白版

在编程中&#xff0c;我们经常需要处理多个任务&#xff0c;这些任务可能需要同时运行&#xff0c;也可能需要按照一定的顺序运行。这就涉及到了线程的概念。线程就像是一个小程序&#xff0c;它可以在程序中独立运行&#xff0c;而且可以和其他线程并行执行。 但是&#xff0…

【python】OpenCV—Local Translation Warps

文章目录 1、功能描述2、原理分析3、代码实现4、效果展示5、完整代码6、参考 1、功能描述 利用液化效果实现瘦脸美颜 交互式的液化效果原理来自 Gustafsson A. Interactive image warping[D]. , 1993. 2、原理分析 上面描述很清晰了&#xff0c;鼠标初始在 C&#xff0c;也即…

STM32标准库学习笔记(十)SPI

前言 学习永无止境&#xff01;本篇是嵌入式开发之片上外设SPI&#xff0c;了解基本硬件原理以及通信协议。 注&#xff1a;本文章为学习笔记&#xff0c;部分图片与文字来源于网络/江协科技课程/手册&#xff0c;如侵权请联系&#xff01;谢谢&#xff01; 一、SPI通信概述 1.…

从github上,下载的android项目,从0-1进行编译运行-踩坑精力,如何进行部署

因为国内的网络原因&#xff0c;一直在anroidstudio开发的问题上&#xff0c;是个每个开发者都会踩坑 一直以为是自己的原因&#xff0c;其实很多都是国内网络的原因&#xff0c;今天就从一个开发者的视角 把从github上一个陌生的项目&#xff0c;如何通过本地就行运行的 首先…

计算机网络 (40)域名系统DNS

前言 计算机网络域名系统DNS&#xff08;Domain Name System&#xff09;是互联网的基础技术之一&#xff0c;它负责将人类可读的域名转换为计算机用来通信的数字IP地址。 一、基本概念 DNS的主要目的是将域名解析或翻译为IP地址&#xff0c;使得用户可以通过简单易记的域名来访…

使用Dify创建个问卷调查的工作流

为啥要使用Dify创建工作流呢&#xff1f;一个基于流程的智能体的实现&#xff0c;特别是基于业务的实现&#xff0c;使用Dify去实现时&#xff0c;通常都是一个对话工作流&#xff0c;当设计到相对复杂一些的流程时&#xff0c;如果将所有逻辑都放在对话工作流中去实现&#xf…

toRef 和 toRefs 详解及应用

在 Vue 3 中&#xff0c;toRef 和 toRefs 是两个用于创建响应式引用的工具&#xff0c;主要用于组合式 API&#xff08;Composition API&#xff09;的场景中 1. toRef 定义 toRef 将某个对象的某个属性包装成一个响应式引用。这样可以直接对该引用进行操作&#xff0c;而不需…

八 rk3568 android11 AP6256 蓝牙调试

一 经典蓝牙 经典蓝牙默认可以工作, 验证可以连接 蓝牙鼠标,键盘, 连接手机等等, 在 系统设置里打开蓝牙 ,扫描设备,配对连接即可。 注: 连接 ANDROID 手机的坑 1 手机连接之后空闲状态会断开 ,变成 配对的设备不是已连接,是正常,使用时又会自动 连接 2 手机传…

解读若依微服务架构图:架构总览、核心模块解析、消息与任务处理、数据存储与缓存、监控与日志

文章目录 1. 引言2. 架构总览3. 核心模块解析3.1 服务注册与配置中心Nacos&#xff1a;微服务的中枢 3.2 网关层ruoyi-gateway&#xff1a;服务的统一入口 3.3 核心业务服务3.4 认证服务ruoyi-auth&#xff1a;认证与授权的守护者 3.5 异构服务整合Sidecar&#xff1a;连接异构…

【MySQL】基础架构分析

考察频率难度40%⭐⭐⭐⭐ 这道题在面试时的出现频率其实并不高&#xff0c;最起码对于笔者来说是没有遇到过。那为什么还是选择把这个问题作为 MySQL 八股文系列的第一个呢&#xff1f;其实原因也挺简单的&#xff0c;还是老规矩&#xff0c;先通过一个问题把整个知识框架来一…

【已解决】【记录】2AI大模型web UI使用tips 本地

docker desktop使用 互动 如果需要发送网页链接&#xff0c;就在链接上加上【#】号 如果要上传文件就点击这个➕号 中文回复 命令它只用中文回复&#xff0c;在右上角打开【对话高级设置】 输入提示词&#xff08;提示词使用英文会更好&#xff09; Must reply to the us…

热烈祝贺“钛然科技”选择使用订单日记

感谢珠海钛然科技有限公司选择使用订单日记&#xff01; 珠海钛然科技有限公司&#xff0c;成立于2020年&#xff0c;位于广东省珠海市高新区&#xff0c;是一家以从事研发和生产功能型纳米高分子涂层为主的企业。 在业务不断壮大的过程中&#xff0c;想使用一种既能提升运营…

Linux-----进程通讯(消息队列)

目录 相关API 1.相关数据类型 mqd_t struct mq_attr struct timespec 2.相关系统调用接口 mq_open() mq_timedsend() && mq_send() mq_timedreceive() && mq_receive() mq_unlink() clock_gettime() 父子进程使用消息队列通讯 平行进程使用消息队列…

【微服务】面试题 5、分布式系统理论:CAP 与 BASE 详解

分布式系统理论&#xff1a;CAP 与 BASE 详解 一、CAP 定理 背景与定义&#xff1a;1998 年由加州大学科学家埃里克布鲁尔提出&#xff0c;分布式系统存在一致性&#xff08;Consistency&#xff09;、可用性&#xff08;Availability&#xff09;、分区容错性&#xff08;Part…

数据结构与算法之二叉树: LeetCode 572. 另一棵树的子树 (Ts版)

另一棵树的子树 https://leetcode.cn/problems/subtree-of-another-tree/description/ 描述 给你两棵二叉树 root 和 subRoot检验 root 中是否包含和 subRoot 具有相同结构和节点值的子树如果存在&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false二叉树 tree …

NineData云原生智能数据管理平台新功能发布|2024年12月版

本月发布 7 项更新&#xff0c;其中重点发布 2 项、功能优化 5 项。 重点发布 数据库 Devops - Oracle 非表对象支持可视化创建与管理 Oracle 非表对象&#xff0c;包括视图&#xff08;View&#xff09;、包&#xff08;Package&#xff09;、存储过程&#xff08;Procedur…

[Unity]MacOS下开发Unity

需要的插件 我使用的是vscode&#xff0c;经过长时间的使用我发现一个问题就是很多插件都是动态的在变化的&#xff0c;不是一成不变的&#xff0c;可能是重构&#xff0c;可能直接换了其他的工具。 所以这个插件也会是更新的状态。 2025年01月08日更新 .NET Install Tool (…

项目开发实践——基于SpringBoot+Vue3实现的在线考试系统(五)

文章目录 一、学生管理模块功能实现1、添加学生功能实现1.1 页面设计1.2 前端功能实现1.3 后端功能实现1.4 效果展示2、学生管理功能实现2.1 页面设计2.2 前端功能实现2.3 后端功能实现2.3.1 后端查询接口实现2.3.2 后端编辑接口实现2.3.3 后端删除接口实现2.4 效果展示二、代码…

实现一个VSCode插件(从创建到发布)

实现一个自己的VSCode 插件 本文将以 yo 为例&#xff0c; 实现一个 VS Code 插件 从创建到发布。 文章目录 实现一个自己的VSCode 插件1. 初始化项目2. 项目结构3. 实现插件功能4. 测试和运行插件5. 发布6. 下载自己发布的插件 1. 初始化项目 首先&#xff0c;我们需要安装 …