<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: CoreNova</title>
    <description>The latest articles on DEV Community by CoreNova (@corenovalabs).</description>
    <link>https://dev.to/corenovalabs</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4059048%2F88cd8ded-abd2-4c2c-be89-7ec2356d1ac3.png</url>
      <title>DEV Community: CoreNova</title>
      <link>https://dev.to/corenovalabs</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/corenovalabs"/>
    <language>en</language>
    <item>
      <title>No LibreOffice, No COM: Three Pure-Python Legacy Office Converters</title>
      <dc:creator>CoreNova</dc:creator>
      <pubDate>Mon, 10 Aug 2026 14:51:26 +0000</pubDate>
      <link>https://dev.to/corenovalabs/no-libreoffice-no-com-three-pure-python-legacy-office-converters-515o</link>
      <guid>https://dev.to/corenovalabs/no-libreoffice-no-com-three-pure-python-legacy-office-converters-515o</guid>
      <description>&lt;p&gt;Legacy Microsoft Office files are still everywhere: document archives, accounting systems, government exports, email attachments, and business workflows that have been running for decades.&lt;/p&gt;

&lt;p&gt;The request sounds simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Convert &lt;code&gt;.doc&lt;/code&gt;, &lt;code&gt;.xls&lt;/code&gt;, and &lt;code&gt;.ppt&lt;/code&gt; files into &lt;code&gt;.docx&lt;/code&gt;, &lt;code&gt;.xlsx&lt;/code&gt;, and &lt;code&gt;.pptx&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But the usual solutions often require Microsoft Office automation, LibreOffice, a platform-specific bridge, or an external conversion service. Those choices can be difficult to deploy in Linux containers, serverless jobs, restricted environments, and offline systems.&lt;/p&gt;

&lt;p&gt;I wanted another option: converters that read the legacy binary formats directly and write Office Open XML using Python.&lt;/p&gt;

&lt;p&gt;That work has grown into three open-source projects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/HuiTurn/doc2docx" rel="noopener noreferrer"&gt;doc2docx&lt;/a&gt;: Word &lt;code&gt;.doc&lt;/code&gt; to &lt;code&gt;.docx&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/HuiTurn/xls2xlsx" rel="noopener noreferrer"&gt;xls2xlsx&lt;/a&gt;: Excel &lt;code&gt;.xls&lt;/code&gt; to &lt;code&gt;.xlsx&lt;/code&gt; or &lt;code&gt;.xlsm&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/HuiTurn/ppt2pptx" rel="noopener noreferrer"&gt;ppt2pptx&lt;/a&gt;: PowerPoint &lt;code&gt;.ppt&lt;/code&gt; to &lt;code&gt;.pptx&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All three use only the Python standard library at runtime. They do not launch Microsoft Office, LibreOffice, COM, Java, or an external conversion process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this is harder than changing a file extension
&lt;/h2&gt;

&lt;p&gt;The old and new Office formats have fundamentally different architectures.&lt;/p&gt;

&lt;p&gt;Word 97–2003, Excel 97–2003, and PowerPoint 97–2003 files are binary formats. They are commonly stored in a Compound File Binary (CFB/OLE) container, but each application has its own internal data model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Word uses document streams and interconnected tables for text, formatting, sections, drawings, and references.&lt;/li&gt;
&lt;li&gt;Excel uses BIFF records for worksheets, formulas, styles, charts, and workbook state.&lt;/li&gt;
&lt;li&gt;PowerPoint uses a hierarchy of records, persist objects, masters, shapes, and incremental-save history.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Modern Office files are OPC/OOXML packages: ZIP containers made of XML parts, media files, relationships, and content-type declarations.&lt;/p&gt;

&lt;p&gt;Conversion therefore means parsing one object model and reconstructing it in another. It is not a byte copy, and the three applications cannot share a single universal conversion engine.&lt;/p&gt;

&lt;p&gt;The projects do share the same product-level conventions—similar commands, Python APIs, reports, batch behavior, and exit codes—but each has its own format-specific parser and writer.&lt;/p&gt;

&lt;h2&gt;
  
  
  The principles behind the projects
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. No third-party runtime dependencies
&lt;/h3&gt;

&lt;p&gt;The conversion path uses the Python standard library. This keeps deployment small and predictable, especially in containers, private networks, CI workers, and environments where installing a full office suite is impractical.&lt;/p&gt;

&lt;p&gt;“No runtime dependencies” does not mean that development tools never use other software. For example, optional regression scripts can use Microsoft Office or LibreOffice to compare rendered output. Those tools verify the converter; they are not used by the converter itself.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Follow the published specifications
&lt;/h3&gt;

&lt;p&gt;The implementations are based primarily on Microsoft's published specifications, including MS-CFB, MS-DOC, MS-XLS, MS-PPT, and MS-ODRAW, together with the relevant OOXML documentation.&lt;/p&gt;

&lt;p&gt;Working from the specifications makes the behavior explainable and testable. It also avoids treating another office application as a black-box conversion engine.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Report loss instead of hiding it
&lt;/h3&gt;

&lt;p&gt;Legacy Office formats contain features that do not map cleanly to OOXML. Some objects are also underspecified, vendor-specific, malformed, or dependent on application behavior.&lt;/p&gt;

&lt;p&gt;The converters produce structured diagnostics for unsupported, repaired, omitted, or approximated content. A conversion that opens successfully should not automatically be presented as a perfect conversion.&lt;/p&gt;

&lt;p&gt;This matters in real migration work: knowing what changed is often as important as producing the new file.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Make file handling safe and automation-friendly
&lt;/h3&gt;

&lt;p&gt;Inputs are opened read-only. Outputs and JSON reports are written through temporary files and atomically replaced after successful serialization. The tools reject dangerous source/output collisions, and batch jobs isolate failures so that one bad file does not stop an entire directory migration.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three converters
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Project&lt;/th&gt;
&lt;th&gt;PyPI distribution&lt;/th&gt;
&lt;th&gt;Command / import&lt;/th&gt;
&lt;th&gt;Conversion&lt;/th&gt;
&lt;th&gt;Python&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/HuiTurn/doc2docx" rel="noopener noreferrer"&gt;doc2docx&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;msdoc2docx&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;doc2docx&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;DOC → DOCX&lt;/td&gt;
&lt;td&gt;3.10+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/HuiTurn/xls2xlsx" rel="noopener noreferrer"&gt;xls2xlsx&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;msxls2xlsx&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;xls2xlsx&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;XLS → XLSX/XLSM&lt;/td&gt;
&lt;td&gt;3.10+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/HuiTurn/ppt2pptx" rel="noopener noreferrer"&gt;ppt2pptx&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ppt2pptx&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ppt2pptx&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;PPT → PPTX&lt;/td&gt;
&lt;td&gt;3.11+&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The distinction between the distribution name and command name is intentional. For example, you install &lt;code&gt;msdoc2docx&lt;/code&gt; from PyPI and then run the &lt;code&gt;doc2docx&lt;/code&gt; command.&lt;/p&gt;

&lt;h3&gt;
  
  
  doc2docx
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;doc2docx&lt;/code&gt; reads Word 97–2003 binary documents and creates native WordprocessingML.&lt;/p&gt;

&lt;p&gt;It currently handles a growing range of document content, including text, common character and paragraph formatting, fonts, styles, lists, tables, sections, headers and footers, notes, comments, bookmarks, fields, pictures, common floating shapes, and confirmed embedded OLE objects.&lt;/p&gt;

&lt;p&gt;It can also open XOR-obfuscated, classic RC4, and RC4 CryptoAPI password-protected documents when a password is supplied.&lt;/p&gt;

&lt;p&gt;Install and convert:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install &lt;/span&gt;msdoc2docx
doc2docx input.doc &lt;span class="nt"&gt;-o&lt;/span&gt; output.docx &lt;span class="nt"&gt;--report&lt;/span&gt; report.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or use the Python API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;doc2docx&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;convert&lt;/span&gt;

&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;convert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input.doc&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;output.docx&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;secret&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_dict&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  xls2xlsx
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;xls2xlsx&lt;/code&gt; parses Excel BIFF workbooks and writes &lt;code&gt;.xlsx&lt;/code&gt; or &lt;code&gt;.xlsm&lt;/code&gt; output.&lt;/p&gt;

&lt;p&gt;Its supported content includes common cell types, formulas and cached values, styles, rich text, merged cells, dimensions, outlines, hyperlinks, comments, conditional formatting, data validation, filters, print settings, images, common charts, basic shapes, OLE payloads, and VBA projects.&lt;/p&gt;

&lt;p&gt;If a workbook contains VBA, the default output is &lt;code&gt;.xlsm&lt;/code&gt; when VBA preservation is enabled.&lt;/p&gt;

&lt;p&gt;This project also takes a second approach to fidelity: in addition to native conversion, it embeds an exact copy of the original &lt;code&gt;.xls&lt;/code&gt; file in the output by default. The source can later be recovered and verified using its stored length and SHA-256 digest.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install &lt;/span&gt;msxls2xlsx
xls2xlsx input.xls &lt;span class="nt"&gt;-o&lt;/span&gt; output.xlsx &lt;span class="nt"&gt;--report&lt;/span&gt; report.json
xls2xlsx recover output.xlsx &lt;span class="nt"&gt;-o&lt;/span&gt; recovered.xls
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Python usage follows the same pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;xls2xlsx&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;convert&lt;/span&gt;

&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;convert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input.xls&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;output.xlsx&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;preserve_styles&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;preserve_vba&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;preserve_source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;warnings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ppt2pptx
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;ppt2pptx&lt;/code&gt; reads the PowerPoint record stream directly and reconstructs slides as a &lt;code&gt;.pptx&lt;/code&gt; package.&lt;/p&gt;

&lt;p&gt;It preserves slide order and dimensions, master relationships, hidden-slide state, master decorations, editable text and common shapes, pictures, backgrounds, comments, speaker notes, slide numbers, dates, headers, and footers. It also reconstructs legacy rectangle-cell tables as editable DrawingML tables in supported cases.&lt;/p&gt;

&lt;p&gt;The parser accounts for PowerPoint's append-only incremental saves, where older document containers may still be present in the file. It resolves current persist objects and the master referenced by each slide instead of assuming that the first master applies everywhere.&lt;/p&gt;

&lt;p&gt;Password-protected RC4 CryptoAPI presentations are supported when a password is provided.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install &lt;/span&gt;ppt2pptx
ppt2pptx presentation.ppt &lt;span class="nt"&gt;-o&lt;/span&gt; presentation.pptx &lt;span class="nt"&gt;--report&lt;/span&gt; report.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;ppt2pptx&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;convert&lt;/span&gt;

&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;convert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;protected.ppt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;protected.pptx&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;secret&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_dict&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  A consistent command-line workflow
&lt;/h2&gt;

&lt;p&gt;Each project supports conversion, read-only inspection, recursive batch processing, and structured reports.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Inspect without creating an output file&lt;/span&gt;
doc2docx inspect input.doc &lt;span class="nt"&gt;--json&lt;/span&gt;
xls2xlsx inspect input.xls &lt;span class="nt"&gt;--json&lt;/span&gt;
ppt2pptx inspect input.ppt &lt;span class="nt"&gt;--json&lt;/span&gt;

&lt;span class="c"&gt;# Convert directory trees&lt;/span&gt;
doc2docx batch ./legacy-docs &lt;span class="nt"&gt;-o&lt;/span&gt; ./modern-docs &lt;span class="nt"&gt;--recursive&lt;/span&gt; &lt;span class="nt"&gt;--report&lt;/span&gt; doc-batch.json
xls2xlsx batch ./legacy-sheets &lt;span class="nt"&gt;-o&lt;/span&gt; ./modern-sheets &lt;span class="nt"&gt;--recursive&lt;/span&gt; &lt;span class="nt"&gt;--report&lt;/span&gt; xls-batch.json
ppt2pptx batch ./legacy-slides &lt;span class="nt"&gt;-o&lt;/span&gt; ./modern-slides &lt;span class="nt"&gt;--recursive&lt;/span&gt; &lt;span class="nt"&gt;--report&lt;/span&gt; ppt-batch.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The common workflow is useful when the tools are called from shell scripts, migration jobs, APIs, or queues. Exit code &lt;code&gt;0&lt;/code&gt; means success, &lt;code&gt;1&lt;/code&gt; represents conversion failure or a partial batch failure, and &lt;code&gt;2&lt;/code&gt; indicates invalid input or command usage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Diagnostics are part of the output
&lt;/h2&gt;

&lt;p&gt;A report can identify warnings and errors at useful locations such as a worksheet cell, binary stream offset, slide index, or object type. Reports also include conversion statistics.&lt;/p&gt;

&lt;p&gt;A simplified report looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/data/input.xls"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"destination"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/data/output.xlsx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"diagnostics"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"statistics"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sheets"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"cells"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"vba_preserved"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"source_archive_preserved"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This lets an application distinguish between outcomes such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the file was converted without a known loss;&lt;/li&gt;
&lt;li&gt;the main content was converted, but an advanced object was approximated;&lt;/li&gt;
&lt;li&gt;a feature was deliberately omitted for safety;&lt;/li&gt;
&lt;li&gt;the input was malformed or unsupported;&lt;/li&gt;
&lt;li&gt;one file failed inside an otherwise successful batch.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where these tools fit
&lt;/h2&gt;

&lt;p&gt;These projects are intended for situations where direct, local, automatable conversion is more valuable than depending on a desktop office application:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;migrating document archives on Linux;&lt;/li&gt;
&lt;li&gt;converting uploads inside an application backend;&lt;/li&gt;
&lt;li&gt;processing files in containers or CI jobs;&lt;/li&gt;
&lt;li&gt;inspecting old Office files without opening them interactively;&lt;/li&gt;
&lt;li&gt;handling documents in offline or restricted networks;&lt;/li&gt;
&lt;li&gt;building conversion pipelines that need machine-readable loss reports.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They are not a claim that every historical Office feature can already be reproduced perfectly. Advanced Word drawing geometry, some Excel charts and controls, and PowerPoint animation, media, SmartArt, and complex grouped objects still have incomplete mappings. The repositories document their current limitations in detail.&lt;/p&gt;

&lt;p&gt;If your requirement is “make this file look right in every edge case,” test the output with representative real documents. If your requirement is “quietly process thousands of files,” keep the JSON diagnostics and review the warning distribution instead of checking only whether a &lt;code&gt;.docx&lt;/code&gt;, &lt;code&gt;.xlsx&lt;/code&gt;, or &lt;code&gt;.pptx&lt;/code&gt; file was created.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I learned
&lt;/h2&gt;

&lt;p&gt;The biggest lesson is that file conversion is not only a parser problem. It is a preservation problem.&lt;/p&gt;

&lt;p&gt;A useful converter must answer several questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What can be translated into an editable native OOXML object?&lt;/li&gt;
&lt;li&gt;What can be preserved as original data even if it cannot yet be translated?&lt;/li&gt;
&lt;li&gt;What must be approximated or omitted?&lt;/li&gt;
&lt;li&gt;How can the caller discover those decisions automatically?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That is why diagnostics, atomic writes, source preservation, and regression testing are first-class features rather than afterthoughts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try the projects
&lt;/h2&gt;

&lt;p&gt;The three converters are open source under the MIT License:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/HuiTurn/doc2docx" rel="noopener noreferrer"&gt;HuiTurn/doc2docx&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/HuiTurn/xls2xlsx" rel="noopener noreferrer"&gt;HuiTurn/xls2xlsx&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/HuiTurn/ppt2pptx" rel="noopener noreferrer"&gt;HuiTurn/ppt2pptx&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have real legacy files that expose a conversion problem, please open an issue in the relevant repository. A small reproducible sample—or a detailed description when the file cannot be shared—is especially helpful.&lt;/p&gt;

&lt;p&gt;Stars are appreciated, but edge cases are even more valuable: old Office files have decades of them.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>doc2docx</category>
      <category>xls2xlsx</category>
      <category>ppt2pptx</category>
    </item>
  </channel>
</rss>
