Automating AnoHAT HXS → JavaHelp Conversion for Documentation Teams

Quick Tutorial: Transform AnoHAT HXS Files into JavaHelp TopicsConverting legacy help formats into modern, maintainable help systems is a common task for documentation teams. This tutorial walks through converting AnoHAT HXS files (compiled HTML Help eXtended sources often used in older Windows help systems) into JavaHelp topics. You’ll learn how to extract content, prepare assets, map structure, and generate JavaHelp-compatible HTML and XML so the resulting help set integrates cleanly with Java applications.


What you’ll need

  • A Windows machine (or VM) with tools to extract HXS/HHC content, or access to the original source HTML.
  • Decompiler/extraction tools (examples: 7-Zip, Microsoft HTML Help Workshop, HxS decompiler tools).
  • A text editor or IDE (VS Code, IntelliJ, or similar).
  • JavaHelp SDK (if you plan to build and test a JavaHelp set locally).
  • Basic knowledge of HTML, CSS, and XML.

Background: HXS vs JavaHelp

  • HXS: A compiled help file format from Microsoft (used by HTML Help and components like Help 2 / HxS); content often includes HTML pages, images, a contents/index file (.hhc, .hhk), and compiled metadata.
  • JavaHelp: An Oracle/Sun help system for Java applications; uses a contents map (map.xml), TOC (toc.xml), search indices, and topic HTML/XHTML pages. JavaHelp expects a specific directory structure and XML configuration (helpset file).

Step 1 — Extract HXS contents

  1. If you have the original source files (HTML, images, CSS), start with those. If only the compiled HXS is available:
    • Try simple extraction with 7‑Zip (right-click → Open archive). Some HXS files can be opened like archives.
    • If that fails, use specialized HXS extraction tools or an HxS decompiler. Search for “HxS decompiler” or “extract HXS contents” to find community tools.
  2. Result: a folder containing topic HTML pages, images, style sheets, and navigation files (for example, .hhc for table of contents, .hhk for index).

Step 2 — Audit and cleanup extracted HTML

  1. Open several topic files to check HTML quality:
    • Look for non‑standard HTML (framesets, ActiveX, script-heavy navigation).
    • Check encoding and character set declarations (convert to UTF‑8 if necessary).
  2. Normalize doctype and structure:
    • Convert legacy markup into well-formed HTML5 or XHTML (JavaHelp works with HTML; XHTML often yields fewer quirks).
    • Remove or adapt deprecated tags (font, center) and inline event handlers that may break in JavaHelp.
  3. Consolidate CSS:
    • Collect all stylesheets and move them into a single styles folder. Adjust relative paths.

Step 3 — Plan the JavaHelp structure

JavaHelp requires a particular file layout and several XML files:

  • helpset (.hs)
  • map file (map.xml) — maps topic IDs to files
  • table of contents (toc.xml)
  • index (index.xml) — optional, but useful
  • search index files — generated by JavaHelp tools
  • /docs — folder with topic HTML/XHTML and assets

Decide how the existing HXS TOC (often .hhc) will map into JavaHelp’s TOC. Keep topic IDs consistent and meaningful (for example, using original HXS anchor names).


Step 4 — Convert TOC and Index

  1. Extract structure from .hhc (TOC):
    • The .hhc file is an HTML-like file with nested or
    • entries. Parse it to capture hierarchy, titles, and file links.
    • Create toc.xml for JavaHelp:
      • For each TOC entry create a with title and target.
      • Example snippet:
        
        <toc version="1.0"> <tocitem text="Introduction" target="docs/intro.html"/> <tocitem text="Getting Started" target="docs/getting_started.html">  <tocitem text="Installation" target="docs/install.html"/> </tocitem> </toc> 
    • For index (.hhk), extract keywords and target pages and convert into JavaHelp index format (index.xml). Each should include terms and the target topic.

    • Step 5 — Create map.xml (topic ID mapping)

      JavaHelp’s map.xml links IDs used by URLs or the TOC to actual files:

      • Choose unique IDs, preferably derived from original anchors or filenames (e.g., intro, install).
      • Example:
        
        <?xml version="1.0" encoding="UTF-8"?> <map version="1.0"> <mapID target="docs/intro.html" name="intro"/> <mapID target="docs/install.html" name="install"/> </map> 
      • Ensure the TOC and index reference these IDs where appropriate.

      Step 6 — Adapt topic pages for JavaHelp

      1. Titles and anchors:
        • Ensure each topic has a clear and, if linking by anchor, include stable id attributes on heading elements.</li> </ul> </li> <li>Relative links: <ul> <li>Convert absolute or HXS-specific links to relative links inside the /docs folder.</li> <li>Replace unsupported scripting navigation with plain HTML anchors or JavaHelp-specific linking (using map ID references).</li> </ul> </li> <li>CSS and assets: <ul> <li>Update stylesheet links: <link rel="stylesheet" href="../styles/main.css"> (adjust relative paths).</li> <li>Copy images and other media into the help set’s assets folder.</li> </ul> </li> </ol> <hr> <h3 id="step-7-build-the-helpset-hs-file">Step 7 — Build the helpset (.hs) file</h3> <p>Create a helpset file (XML) that references your TOC, map, and index:</p> <pre><code ><?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE helpset PUBLIC "-//Sun Microsystems Inc.//DTD JavaHelp Helpset Version 2.0//EN" "http://java.sun.com/products/javahelp/helpset_2_0.dtd"> <helpset version="2.0"> <title>Your Product Help</title> <maps> <homeID>intro</homeID> <mapref location="map.xml"/> </maps> <view> <name>TOC</name> <label>Contents</label> <type>javax.help.TOCView</type> <data>toc.xml</data> </view> <view> <name>Index</name> <label>Index</label> <type>javax.help.IndexView</type> <data>index.xml</data> </view> </helpset> </code></pre> <p>Place this helpset file at the root of the help set folder.</p> <hr> <h3 id="step-8-generate-search-indices-and-test">Step 8 — Generate search indices and test</h3> <ol> <li>If you have the JavaHelp SDK or tools, run the indexer to create search indices for full‑text search.</li> <li>Load the helpset in a Java application or a simple JavaHelp viewer: <ul> <li>Use HelpSet class to load by file URL and open the help broker.</li> </ul> </li> <li>Navigate the TOC, search, and ensure links open correct topics and assets load.</li> </ol> <hr> <h3 id="troubleshooting-common-issues">Troubleshooting common issues</h3> <ul> <li>Broken images or CSS: Fix relative paths; ensure assets copied into help set.</li> <li>Encoding problems: Convert files to UTF‑8; declare correct charset in meta tags.</li> <li>Missing anchors: Add id attributes to headings or create HTML anchor elements.</li> <li>Scripted navigation failing: Replace with static links or JavaHelp map‑based linking.</li> </ul> <hr> <h3 id="automation-and-scripting-tips">Automation and scripting tips</h3> <ul> <li>Use small scripts (Python, Node.js) to: <ul> <li>Parse .hhc/.hhk and generate toc.xml and index.xml.</li> <li>Batch-convert HTML encoding and fix links.</li> <li>Copy and reorganize assets into the JavaHelp folder structure.</li> </ul> </li> <li>Example Python approach: use BeautifulSoup to parse .hhc and create XML templates.</li> </ul> <hr> <h3 id="final-checklist-before-deployment">Final checklist before deployment</h3> <ul> <li>All topics present, titles correct, and metadata accurate.</li> <li>map.xml IDs match TOC and index references.</li> <li>Assets (CSS, images) load correctly.</li> <li>Search index generated and working.</li> <li>Helpset tested inside target Java application.</li> </ul> <hr> <p>Converting AnoHAT HXS to JavaHelp requires careful extraction, cleanup, and mapping of structure. With a reproducible process (scripts for parsing .hhc/.hhk and fixing links) you can convert large help sets reliably and preserve navigation and indexing for Java applications.</p> </div> <div class="wp-block-group has-global-padding is-layout-constrained wp-block-group-is-layout-constrained" style="padding-top:var(--wp--preset--spacing--60);padding-bottom:var(--wp--preset--spacing--60)"> </div> <div class="wp-block-group alignwide is-layout-flow wp-block-group-is-layout-flow" style="margin-top:var(--wp--preset--spacing--60);margin-bottom:var(--wp--preset--spacing--60);"> <nav class="wp-block-group alignwide is-content-justification-space-between is-nowrap is-layout-flex wp-container-core-group-is-layout-9b36172e wp-block-group-is-layout-flex" aria-label="Post navigation" style="border-top-color:var(--wp--preset--color--accent-6);border-top-width:1px;padding-top:var(--wp--preset--spacing--40);padding-bottom:var(--wp--preset--spacing--40)"> <div class="post-navigation-link-previous wp-block-post-navigation-link"><span class="wp-block-post-navigation-link__arrow-previous is-arrow-arrow" aria-hidden="true">←</span><a href="http://cloud934221.lat/level-up-productivity-a-beginners-guide-to-macromaker/" rel="prev">Level Up Productivity: A Beginner’s Guide to MacroMaker</a></div> <div class="post-navigation-link-next wp-block-post-navigation-link"><a href="http://cloud934221.lat/exploring-mysql-a-modern-c-wrapper-for-mysql/" rel="next">Exploring MySQL++: A Modern C++ Wrapper for MySQL</a><span class="wp-block-post-navigation-link__arrow-next is-arrow-arrow" aria-hidden="true">→</span></div> </nav> </div> <div class="wp-block-comments wp-block-comments-query-loop" style="margin-top:var(--wp--preset--spacing--70);margin-bottom:var(--wp--preset--spacing--70)"> <h2 class="wp-block-heading has-x-large-font-size">Comments</h2> <div id="respond" class="comment-respond wp-block-post-comments-form"> <h3 id="reply-title" class="comment-reply-title">Leave a Reply <small><a rel="nofollow" id="cancel-comment-reply-link" href="/automating-anohat-hxs-%e2%86%92-javahelp-conversion-for-documentation-teams/#respond" style="display:none;">Cancel reply</a></small></h3><form action="http://cloud934221.lat/wp-comments-post.php" method="post" id="commentform" class="comment-form"><p class="comment-notes"><span id="email-notes">Your email address will not be published.</span> <span class="required-field-message">Required fields are marked <span class="required">*</span></span></p><p class="comment-form-comment"><label for="comment">Comment <span class="required">*</span></label> <textarea id="comment" name="comment" cols="45" rows="8" maxlength="65525" required></textarea></p><p class="comment-form-author"><label for="author">Name <span class="required">*</span></label> <input id="author" name="author" type="text" value="" size="30" maxlength="245" autocomplete="name" required /></p> <p class="comment-form-email"><label for="email">Email <span class="required">*</span></label> <input id="email" name="email" type="email" value="" size="30" maxlength="100" aria-describedby="email-notes" autocomplete="email" required /></p> <p class="comment-form-url"><label for="url">Website</label> <input id="url" name="url" type="url" value="" size="30" maxlength="200" autocomplete="url" /></p> <p class="comment-form-cookies-consent"><input id="wp-comment-cookies-consent" name="wp-comment-cookies-consent" type="checkbox" value="yes" /> <label for="wp-comment-cookies-consent">Save my name, email, and website in this browser for the next time I comment.</label></p> <p class="form-submit wp-block-button"><input name="submit" type="submit" id="submit" class="wp-block-button__link wp-element-button" value="Post Comment" /> <input type='hidden' name='comment_post_ID' value='183' id='comment_post_ID' /> <input type='hidden' name='comment_parent' id='comment_parent' value='0' /> </p></form> </div><!-- #respond --> </div> </div> <div class="wp-block-group alignwide has-global-padding is-layout-constrained wp-block-group-is-layout-constrained" style="padding-top:var(--wp--preset--spacing--60);padding-bottom:var(--wp--preset--spacing--60)"> <h2 class="wp-block-heading alignwide has-small-font-size" style="font-style:normal;font-weight:700;letter-spacing:1.4px;text-transform:uppercase">More posts</h2> <div class="wp-block-query alignwide is-layout-flow wp-block-query-is-layout-flow"> <ul class="alignfull wp-block-post-template is-layout-flow wp-container-core-post-template-is-layout-3ee800f6 wp-block-post-template-is-layout-flow"><li class="wp-block-post post-757 post type-post status-publish format-standard hentry category-uncategorised"> <div class="wp-block-group alignfull is-content-justification-space-between is-nowrap is-layout-flex wp-container-core-group-is-layout-154222c2 wp-block-group-is-layout-flex" style="border-bottom-color:var(--wp--preset--color--accent-6);border-bottom-width:1px;padding-top:var(--wp--preset--spacing--30);padding-bottom:var(--wp--preset--spacing--30)"> <h3 class="wp-block-post-title has-large-font-size"><a href="http://cloud934221.lat/unleashing-the-power-of-the-stickymouse-2000-a-game-changer-in-ergonomic-design/" target="_self" >Unleashing the Power of the StickyMouse 2000: A Game-Changer in Ergonomic Design</a></h3> <div class="has-text-align-right wp-block-post-date"><time datetime="2025-09-06T13:26:22+01:00"><a href="http://cloud934221.lat/unleashing-the-power-of-the-stickymouse-2000-a-game-changer-in-ergonomic-design/">6 September 2025</a></time></div> </div> </li><li class="wp-block-post post-756 post type-post status-publish format-standard hentry category-uncategorised"> <div class="wp-block-group alignfull is-content-justification-space-between is-nowrap is-layout-flex wp-container-core-group-is-layout-154222c2 wp-block-group-is-layout-flex" style="border-bottom-color:var(--wp--preset--color--accent-6);border-bottom-width:1px;padding-top:var(--wp--preset--spacing--30);padding-bottom:var(--wp--preset--spacing--30)"> <h3 class="wp-block-post-title has-large-font-size"><a href="http://cloud934221.lat/portable-spyme-tools-essential-gear-for-discreet-surveillance/" target="_self" >Portable SpyMe Tools: Essential Gear for Discreet Surveillance</a></h3> <div class="has-text-align-right wp-block-post-date"><time datetime="2025-09-06T12:57:02+01:00"><a href="http://cloud934221.lat/portable-spyme-tools-essential-gear-for-discreet-surveillance/">6 September 2025</a></time></div> </div> </li><li class="wp-block-post post-755 post type-post status-publish format-standard hentry category-uncategorised"> <div class="wp-block-group alignfull is-content-justification-space-between is-nowrap is-layout-flex wp-container-core-group-is-layout-154222c2 wp-block-group-is-layout-flex" style="border-bottom-color:var(--wp--preset--color--accent-6);border-bottom-width:1px;padding-top:var(--wp--preset--spacing--30);padding-bottom:var(--wp--preset--spacing--30)"> <h3 class="wp-block-post-title has-large-font-size"><a href="http://cloud934221.lat/unlocking-the-power-of-memplus-a-comprehensive-guide/" target="_self" >Unlocking the Power of MemPlus: A Comprehensive Guide</a></h3> <div class="has-text-align-right wp-block-post-date"><time datetime="2025-09-06T12:30:21+01:00"><a href="http://cloud934221.lat/unlocking-the-power-of-memplus-a-comprehensive-guide/">6 September 2025</a></time></div> </div> </li><li class="wp-block-post post-754 post type-post status-publish format-standard hentry category-uncategorised"> <div class="wp-block-group alignfull is-content-justification-space-between is-nowrap is-layout-flex wp-container-core-group-is-layout-154222c2 wp-block-group-is-layout-flex" style="border-bottom-color:var(--wp--preset--color--accent-6);border-bottom-width:1px;padding-top:var(--wp--preset--spacing--30);padding-bottom:var(--wp--preset--spacing--30)"> <h3 class="wp-block-post-title has-large-font-size"><a href="http://cloud934221.lat/getflvplay/" target="_self" >GetFlvPlay</a></h3> <div class="has-text-align-right wp-block-post-date"><time datetime="2025-09-06T12:07:02+01:00"><a href="http://cloud934221.lat/getflvplay/">6 September 2025</a></time></div> </div> </li></ul> </div> </div> </main> <footer class="wp-block-template-part"> <div class="wp-block-group has-global-padding is-layout-constrained wp-block-group-is-layout-constrained" style="padding-top:var(--wp--preset--spacing--60);padding-bottom:var(--wp--preset--spacing--50)"> <div class="wp-block-group alignwide is-layout-flow wp-block-group-is-layout-flow"> <div class="wp-block-group alignfull is-content-justification-space-between is-layout-flex wp-container-core-group-is-layout-e5edad21 wp-block-group-is-layout-flex"> <div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex"> <div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:100%"><h2 class="wp-block-site-title"><a href="http://cloud934221.lat" target="_self" rel="home">cloud934221.lat</a></h2> </div> <div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow"> <div style="height:var(--wp--preset--spacing--40);width:0px" aria-hidden="true" class="wp-block-spacer"></div> </div> </div> <div class="wp-block-group is-content-justification-space-between is-layout-flex wp-container-core-group-is-layout-570722b2 wp-block-group-is-layout-flex"> <nav class="is-vertical wp-block-navigation is-layout-flex wp-container-core-navigation-is-layout-fe9cc265 wp-block-navigation-is-layout-flex"><ul class="wp-block-navigation__container is-vertical wp-block-navigation"><li class=" wp-block-navigation-item wp-block-navigation-link"><a class="wp-block-navigation-item__content" href="#"><span class="wp-block-navigation-item__label">Blog</span></a></li><li class=" wp-block-navigation-item wp-block-navigation-link"><a class="wp-block-navigation-item__content" href="#"><span class="wp-block-navigation-item__label">About</span></a></li><li class=" wp-block-navigation-item wp-block-navigation-link"><a class="wp-block-navigation-item__content" href="#"><span class="wp-block-navigation-item__label">FAQs</span></a></li><li class=" wp-block-navigation-item wp-block-navigation-link"><a class="wp-block-navigation-item__content" href="#"><span class="wp-block-navigation-item__label">Authors</span></a></li></ul></nav> <nav class="is-vertical wp-block-navigation is-layout-flex wp-container-core-navigation-is-layout-fe9cc265 wp-block-navigation-is-layout-flex"><ul class="wp-block-navigation__container is-vertical wp-block-navigation"><li class=" wp-block-navigation-item wp-block-navigation-link"><a class="wp-block-navigation-item__content" href="#"><span class="wp-block-navigation-item__label">Events</span></a></li><li class=" wp-block-navigation-item wp-block-navigation-link"><a class="wp-block-navigation-item__content" href="#"><span class="wp-block-navigation-item__label">Shop</span></a></li><li class=" wp-block-navigation-item wp-block-navigation-link"><a class="wp-block-navigation-item__content" href="#"><span class="wp-block-navigation-item__label">Patterns</span></a></li><li class=" wp-block-navigation-item wp-block-navigation-link"><a class="wp-block-navigation-item__content" href="#"><span class="wp-block-navigation-item__label">Themes</span></a></li></ul></nav> </div> </div> <div style="height:var(--wp--preset--spacing--70)" aria-hidden="true" class="wp-block-spacer"></div> <div class="wp-block-group alignfull is-content-justification-space-between is-layout-flex wp-container-core-group-is-layout-91e87306 wp-block-group-is-layout-flex"> <p class="has-small-font-size">Twenty Twenty-Five</p> <p class="has-small-font-size"> Designed with <a href="https://en-gb.wordpress.org" rel="nofollow">WordPress</a> </p> </div> </div> </div> </footer> </div> <script type="speculationrules"> {"prefetch":[{"source":"document","where":{"and":[{"href_matches":"\/*"},{"not":{"href_matches":["\/wp-*.php","\/wp-admin\/*","\/wp-content\/uploads\/*","\/wp-content\/*","\/wp-content\/plugins\/*","\/wp-content\/themes\/twentytwentyfive\/*","\/*\\?(.+)"]}},{"not":{"selector_matches":"a[rel~=\"nofollow\"]"}},{"not":{"selector_matches":".no-prefetch, .no-prefetch a"}}]},"eagerness":"conservative"}]} </script> <script src="http://cloud934221.lat/wp-includes/js/comment-reply.min.js?ver=6.8.2" id="comment-reply-js" async data-wp-strategy="async"></script> <script id="wp-block-template-skip-link-js-after"> ( function() { var skipLinkTarget = document.querySelector( 'main' ), sibling, skipLinkTargetID, skipLink; // Early exit if a skip-link target can't be located. if ( ! skipLinkTarget ) { return; } /* * Get the site wrapper. * The skip-link will be injected in the beginning of it. */ sibling = document.querySelector( '.wp-site-blocks' ); // Early exit if the root element was not found. if ( ! sibling ) { return; } // Get the skip-link target's ID, and generate one if it doesn't exist. skipLinkTargetID = skipLinkTarget.id; if ( ! skipLinkTargetID ) { skipLinkTargetID = 'wp--skip-link--target'; skipLinkTarget.id = skipLinkTargetID; } // Create the skip link. skipLink = document.createElement( 'a' ); skipLink.classList.add( 'skip-link', 'screen-reader-text' ); skipLink.id = 'wp-skip-link'; skipLink.href = '#' + skipLinkTargetID; skipLink.innerText = 'Skip to content'; // Inject the skip link. sibling.parentElement.insertBefore( skipLink, sibling ); }() ); </script> </body> </html>