Git Product home page Git Product logo

wordinator's Introduction

The Wordinator

Version 1.2.0

Generate high-quality Microsoft Word DOCX files using a simplified XML format (simple word processing XML).

Simple Word Processing XML (SWPX) makes it relatively easy to transform structured content into DOCX (or other similar word processing formats).

The Wordinator uses the Apache POI library to generate DOCX files from SWPX XML. It uses Saxon for XSLT transformations when using the built-in XSLT support.

This approach provides a two-stage X-to-DOCX conversion process, where the first stage is a transform from whatever your input is into one or more SWPX documents and the second stage generates DOCX files from the SWPX files. You can think of the SWPX XML as a very abstract API to the DOCX format.

The Wordinator Java code can run an XSLT to generate the SWPX dynamically from any XML input or you can generate the SWPX documents separately using whatever means you choose and then process those into DOCX files. Word style definitions are managed using a normal Word template (DOTX) file that you create and manage normally.

The Wordinator is designed for batch or on-demand generation of DOCX files.

The Wordinator requires Java 9 or newer (because POI 5 requires it).

The Wordinator provides a generic HTML5-to-DOCX transform that can easily be adapted to your specific HTML or other XML format.

The main challenges are managing white space within text runs and mapping source elements to the appropriate paragraph and character styles. The XSLT has been designed to make the element-to-style mapping as easy as possible by using a separate XSLT mode to generate the style names for elements. This mode uses an XSLT 3 map to map HTML class values to Word style names and paragraph and run-level formatting controls (e.g., a @class token of 'bold' will result in bold runs). This makes configuring the mapping about as easy as it can be. If a simple class-to-style mapping is insufficient you can use normal XSLT templates to map elements in context to styles.

You can use your own XSLT transform to generate SWPX files from any XML (or JSON source for that matter). You may find it easier to generate HTML and then use that as input to the Wordinator.

If you need to go from Word documents back to XML, you may find the DITA for Publishers Word-to-DITA framework useful. This is packaged as a DITA Open Toolkit plugin but is really a general-purpose XML-to-DOCX framework. It does not depend on the DITA Open Toolkit in any way. While it is designed to generate DITA XML it can be adapted to produce any XML format, either directly or through a DITA-to-X transform applied

Release Notes

  • 1.2.0

    • Upgrade to POI 5. Addresses security issues with POI4 and makes newer POI features available for future.
    • Support generation of MathML equations in Word. Thanks to Lars Marius Garshol for contributing this enhancment. This adds the MathML 2 and 3 RNG grammars (courtesy of Ken Holman) for use with the SWPX grammar. For validation, use the simplewpml-mathml3.rng or -mathml2.rng files as the top-level grammar. Requires that you get the MATHML2OOML.XSL from a Microsoft Office distribution if you want to use XSLT to convert MathML into OOXML.
    • Various test case refinements
    • Issue 82: Handle specified-but-empty @height and @width attribute
    • Implement support for document properties (core, extendeded, custom).
  • 1.1.2

    • Reworked release package to put dependency jars in lib/ dir and use non-all-dependencies jar to avoid issue with log4j multi-version jar not working right.
  • 1.1.1

    • Issue 72: Support web and data: URLs for images
    • Issue 73: Restore default table layout to "auto" (corrects narrow columns for tables that don't specify @layout)
  • 1.1.0

    • Issue 42: Implemented generation of Table of Contents field
  • 1.0.4

    • Issue 29: Support literal callouts and reference callouts for footnotes. Added new attributes to fn element for specifying the callout and, optionally, reference callout text.
    • Issue 30: Support cell border color. Added new attributes @bordercolor, @bordercolortop, , @bordercolorbottom, @bordercolorleft, and @bordercolorright. Normal Word border precedence rules apply so it's up to the SWPX file to specify the appropriate values on the appropriate cells to get the desired rendered effect.
  • 1.0.3

    • Issue 11: Added support for catalog resolution with Saxon. Added new command-line option -k/-catalog that specifies a list of catalog files as for Saxon's -catalog option.
    • Issue 15: Copy numbering definitions to the generated DOCX. Resolves issue with list paragraphs not having bullets or numbers when they should.
    • Issue 16: Scale images proportionally when only one dimension is specified in the SWPX.
    • Issue 18: Recognize "both" as a synonym for "justify" in base HTML-to-SWZPX tranform. Use "both" rather than "distribute" for "justify" in generated DOCX.
    • Fixed issue with failure when using Saxon 9.9+ (failure to set global XSLT context).
    • Upgraded to Saxon 10.0 HE and POI 4.1.2
  • 1.0.2

    • Accidently skipped 1.0.2 by releasing the 1.0.2 code as 1.0.3.
  • 1.0.1

    • Fixed issue #: Failure when align value is "justify" or "char" on table cell
  • 1.0.0

    • Section-specific running heads and feet, page geomentry, page numbers
    • Improved table generation:
      • Table spans should be 100% correct
      • Use table styles
      • Correct setting of row- and cell-level vertical spacing
      • Borders on tables and cells
      • Support "shade" property on cells (background color)
    • Fixed issue where runs after footnotes were dropped.
  • 0.9.2

    • Control table borders for rowsep, colsep, and per-edge on individual cells
    • Handle absolute width values on tables
  • 0.9.1

    • Out-of-the-box DITA HTML5 transform.
    • Handle unnamespaced HTML5
    • Added some useful documentation
    • Added command-line help
  • 0.9.0

Working for XHTML input. DOCX pretty complete

  • 0.8.0

Use final version of POI 4.0.0

  • 0.7.0

    • Improved performance by only reading template doc once

Word feature support

The Wordinator supports generation of documents with the following Word features:

  • Paragraphs and runs with specific styles
  • Footnotes and end notes
  • Tables with spans
  • Embedded graphics
  • Running heads and feet
  • Bookmarks
  • Hyperlinks
  • Multiple sections with section-specific running heads and feet, page geometry
  • Formulas using MathML (see below)

Getting Started

The Wordinator is packaged as a runable Java JAR file. It also requires an XSLT transform and a Word DOTX template in addition to your input file.

To try it you can use the basic XHTML- or HTML5-to-DOCX transform that is included in the Wordinator materials. For production use you will need to create your own transform that expresses the details of mapping from your XML or HTML to your styles. This can be pretty easy to implement though--you shouldn't normally need any significant XSLT knowledge.

Installation

Unzip the release package into a convenient location. The release includes the Wordinator JAR file and base XSLT tranforms, along with a generic Word template (as a convenience).

You need to be able run the java command using Java 8 or newer.

If you have ant installed you can run the Wordinator using the build.xml script in the root of the distributaion package (src/main/ant/build.xml in the project source).

Running the Wordinator With Ant

The build.xml file in the distribution provides two targets: html2docx and ditahtml2docx. The default target is ditahtml2docx.

If you just run the ant command from the Wordinator distribution directory it will run the ditahtml2docx target against the sample HTML file included in the distribution:

c:\projects\wordinator> ant
Buildfile: /Users/ekimber/workspace/wordinator/dist/wordinator/build.xml

init:

ditahtml2docx:
     [java] + 2019-03-07 22:14:54,322 [INFO ] Input document or directory='/Users/ekimber/workspace/wordinator/dist/wordinator/html/sample_web_page.html'
     [java] + 2019-03-07 22:14:54,324 [INFO ] Output directory           ='/Users/ekimber/workspace/wordinator/dist/wordinator/out'
     [java] + 2019-03-07 22:14:54,324 [INFO ] DOTX template              ='/Users/ekimber/workspace/wordinator/dist/wordinator/docx/Test_Template.dotx'
     [java] + 2019-03-07 22:14:54,324 [INFO ] XSLT template              ='/Users/ekimber/workspace/wordinator/dist/wordinator/xsl/ditahtml2docx/ditahtml2docx.xsl'
     [java] + 2019-03-07 22:14:54,325 [INFO ] Chunk level                ='root'
...
    [java] + 2019-03-07 22:14:55,759 [INFO ] Generating DOCX file "/Users/ekimber/workspace/wordinator/dist/wordinator/out/sample_web_page.docx"
     [java] + 2019-03-07 22:14:56,249 [INFO ] Transform applied.

BUILD SUCCESSFUL
Total time: 4 seconds

Edit the build.xml file to see the properties you can set to specify your own values for the command-line parameters.

You can create a file named build.properties in the same directory as the build.xml file to set properties statically or you can specify them using -D parameters to the ant command:

c:\projects\wordinator> ant -Dditahtml2docx.dotx=myTemplate.dotx

Running the Wordinator From OxygenXML

You can set up an Oxygen Ant transformation scenario and apply it against HTML files to generate DOCX files from them.

To set up a transformation scenario follow these steps:

  1. Open an HTML file in OxygenXML
  2. Open the Configure Transformation Scenarios dialog
  3. Select "New" and then "Ant transformation"
  4. Give the scenario a meaningful title, i.e. "DITA HTML to DOCX"
  5. In the "Build file" field put the path and name of the build.xml file. Take the defaults for the other fields in this tab.
  6. Switch to the "Parameters" tab and add the following parameters:
    • input.html: ${cfd}/${cfne}
    • output.dir: ${cfd}/out
    • html2docx.dotx: Path to your DOTX file
    • html2docx.xsl: Path to your XSLT (if you have one, otherwise omit)
  7. Switch to the "Output" tab and set the Output field to ${cfd}/out/${cfn}.docx. Make sure that "Open in system application" is selected.

You can omit any of the parameters that you have set using a build.properties file.

You should now be able to run the scenario against any HTML file and have the resulting DOCX file open in Microsoft Word.

Running the Wordinator From The Command Line

  1. Open a command window and navigate to the directory you unzipped the Wordinator package into:
cd c:\projects\wordinator
  1. Run this command:
java -jar wordinator.jar -i html/sample_web_page.html -o out -x xsl/html2docx/html2docx.xsl -t docx/Test_Template.dotx

You should see a lot of messages, ending with this:

+ 2019-03-07 16:58:33,873 [INFO ] Generating DOCX file "/Users/ekimber/workspace/wordinator/dist/wordinator/out/sample_web_page.docx"
+ 2019-03-07 16:58:34,406 [INFO ] Transform applied.
  1. Open the file out\sample_web_page.docx in Microsoft Word

It's not a very pretty test but it demonstrates that the tool is working.

Wordinator Commandline Options

  • -i The input XML file or directory
  • -o The output directory
  • -t The DOTX Word template
  • -x (optional) The XSLT transform to apply to the input file to generate SWPX files.
  • -k (optional) Semicolon-separated list of catalog files to use with Saxon. Same as Saxon's -catalog option.
  • -c (optional) Chunk level. Specifies the section level or type to create separate DOCX files for. The value to use is determined by the details of the XSLT transform (local:is-chunk() function).

If the -i parameter is a directory then it looks for *.swpx files and generates a DOCX file for each one.

Adapting Wordinator To Your Needs

The base HTML-to-DOCX transform is very basic and is not intended to be used as is.

To create good results for your content you will need the following:

  • A Word template (DOTX) that defines the named styles you need to achieve your in-Word styling requirements. For many documents the built-in Word styles will suffice. You may also have existing templates that that you need to map to. The important thing for the mapping to Word is the style names: the mapping from your input XML to Word is in terms of named paragraph, character, and table styles.
  • A custom XSLT style sheet that implements the mapping from your input XML to Simple Word Procesing XML that is then the input to the DOCX generation phase. A a minimum you need to provide the mapping from element type names and @class values to paragraph and character style names. This can be done with a relatively simple XSLT module that overrides the base HTML-to-DOCX transform.
  • The XML from which you will generate the Word documents. This can be any XML but the Wordinator-provided transforms are set up for XHTML and HTML5, so if you are either authoring in HTML5 or you can generate XHTML or HTML5 from your XML then the transform is relatively simple. For example, the provided ditahtml2docx transform handles the HTML5 produced by the DITA Open Toolkit.

Java Integration

The release package uses a jar that contains all the dependency jars required by the Wordinator.

However, if you want to include the Wordinator in a larger application where the dependencies should be managed as separate JAR files, you can build the JAR from the project source.

The Wordinator project is a Maven project.

SimpleWP XML (SWPX)

The Simple Word Processing XML format is the direct input to the DOCX generation phase of the Wordinator.

It is essentially a simplification of Word's internal XML format.

The SWPX format is defined in the simplewpml.rng file in the doctypes/simplewpml directory. The RNG file includes documentation on the SWPX elements and attributes and how to use them.

The XSLT file xsl/html2docx/baseProcessing.xsl does most of the work of generating SWPX from HTML and it also serves to demonstrate how to generate SWPX if you want to implement direct generation from some other XML format.

If you are generating SWPX files be sure to validate them against the simplewpml.rng grammar. One easy way to do this is to use Oxygen to associate the RNG with the the SWPX file using the Document -> Schema -> Associate Schema menu.

Table Spans and Column Widths

The Wordinator supports complex table spans and will correctly calculate the width of cells that span multiple columns.

However, there is a limitation in how the table's column widths are specified: All the values involved in calculating the width of a spanned cell must be of the same type, either all explicit widths or all percentage widths.

This is because at the time the table is generated Wordinator does not know how wide the table will be and therefore cannot convert a mix of absolute and percentage values into absolute values.

When all the values are percentages the resulting Word is generated with percentage values, allowing Word to correctly calculate the widths of the cells. When all the values are absolute then calculation of the spanned width is simple math.

As a rule, it is best to use percentages for table column widths.

If you have tables with a mix of percentage and absolute values for column widths and you have cells that span columns where the widths involved are mixed, Wordinator issues a warning message. The resulting table will likely not be correct.

Vertical (Row) Spans in Tables

Wordinator supports vertical (row) spanning but requires that every cell in the vertical span be accounted for using <vspan/> markers in the <td> elements.

Thus, if a cell in row 1 specifies rowspan of 3, the next two rows must have <td><vspan/></td> elements in that column.

Likewise, if the cells are also horizontally spanned, each placeholder cell must specify the same colspan value as the first cell that specifies rowspan.

Using MathML

If your SimpleWPML document includes MathML markup (see the simplewpml-mathml3.rng grammar), then Wordinator will attempt to use the XSLT transform MML2OMML.XSL to transform the MathML into OOML markup before adding it to the DOCX.

The MML2OMML.xsl transform is not open-source and so cannot be included in the release package. However, you should be able to find it in any Microsoft Office distribution, i.e.:

/Applications/Microsoft Office 2011/Microsoft Word.app/Contents/Resources/mathml2omml.xsl

To make the transform available to Wordinator, name it "MML2OMML.XSL" and put it in a directory on the Java class path. You must run Wordinator using the -cp Java option, you can't use the -jar option as it will ignore the class path.

Customizing the HTML-to-SWPX Transforms

The module xsl/html2docx/get-style-name.xsl implements the default mapping from HTML elements to style names. It uses a variable that is a map from @class attribute values to Word style names:

  <xsl:variable name="classToStyleNameMap" as="map(xs:string, xs:string)">
    <xsl:map>
      <xsl:map-entry key="'p1'" select="'Paragraph 1'"/>
    </xsl:map>
  </xsl:variable>

Each <xsl:map-entry> element maps a @class name (key="'p1'") to a style name (select="'Paragraph 1'").

You can override this variable in a custom XSLT to add your own mapping.

Note that the values of the @key and @select attributes are XSLT string literals: 'p1' and 'Paragraph 1'. Note the straight single quotes (') around the strings. If you forget those your results will be strange.

The map variable is used like so:

  <xsl:template mode="get-style-name" match="xhtml:span[@class] | xhtml:p[@class]" as="xs:string?">
    <xsl:param name="doDebug" as="xs:boolean" tunnel="yes" select="false()"/>
    
    <xsl:variable name="tokens" as="xs:string*" select="tokenize(@class, ' ')"/>
    <xsl:variable name="key" select="$tokens[1]"/>
    <xsl:variable name="styleName" as="xs:string?"
      select="map:get($classToStyleNameMap, $key)"
    />
    <xsl:sequence select="if (exists($styleName)) then $styleName else ()"/>
  </xsl:template>

Here, the @class attribute of the element that matches the template is tokenized on blank spaces and then the first value is used to look up an entry in the $classToStyleNameMap variable.

TBD: More guidance on customizing the mapping. Would also be easy to implement using a JSON file to define the mapping as a separate configuration file.

Managing Word Styles

The Wordinator requires a Word template document (DOTX) that defines the styles available in the generated Word document.

To create and manage styles use this general procedure:

  1. Create a Word document with the styles you need. For every style, whether built-in or custom, create at least one object (paragraph, character run, table, etc.) that uses the style.
  2. Save the document as a DOTX (Word template document). This will be the template you provide to the Wordinator.
  3. To add or modify styles, create a new document from the DOTX file. Going forward you will use this new file to create new styles or modify existing styles.
  4. When you create or modify styles in the document, be sure to check the "Add to template" check mark on the style dialog. This will cause the template document to be updated with the new style information when you save the document you are editing.

A Note About Latent Styles

Word has the concept of "latent styles". These are styles where the name is defined but the style definition does not actually exist in the document. You might reasonably think that you can specify latent style names in your SWPX and have them become real styles when Word opens the generated DOCX file. Unfortunately that is not possible.

Latent styles are mapped to real styles by magic inside Word. This means that there is no way to know, a-priori, what the style ID will be for the style ultimately created for a latent style. In addition, the style ID that Word uses will depend on a number of variables, including the version of Word, the locale, and so on.

Thus, it is impossible for Wordinator (or any processor other than Word itself) to go from the name of a latent style to a real style by its Word-assigned style ID.

That is, given the name of a latent style (which you can look up in the styles.xml file in the list of latent styles) there is no way to generate a style ID that will reliably resolve to a real style when Word opens the document. Even if you create the real style and get the ID Word assigned to it on your machine, that ID may be different in different environments, especially in different locales.

This means that all styles used in your SWPX file must also be real styles in your DOTX file.

The behavior when you have specified a latent style name in the SWPX is that the resulting paragraph or run will not have any style associated with it (because the lookup of the style by name will fail because the style doesn't exist in the template).

See issue 23: Reporting the use of a latent style requires an enhancement to POI first.

Using the Style Organizer

If you forget to do "Add to template" or you want to copy styles from an existing Word document, you can use the style organizer.

To get to the style organizer:

  1. Select Tools->Templates and Add-ins to bring up the Tools and Add-ins dialog The dialog shows the template associated with the document. If your template is not attached, use the Attach button to attach it. Make sure the "Automatically update document styles" check box is checked.
  2. Click the "Organizer" button to open the Organizer dialog. Select the "Styles" tab
  3. The right side of the Organizer dialog shows the template document to which you will copy styles. It probably shows the default template. If so, click "Close file" and then click "Open file" and select your DOTX file.
  4. Use the Organizer dialog to copy styles from the left side to the right side.
  5. Click "Close" to save your changes to the template document.

Math support in Wordinator

The SWPX schema by default does not allow MathML to be included, because the Wordinator does not support it out of the box. However, the src/main/doctypes/simplewpml/simplewpml.rng schema is just a shell that includes simplewpml-base.rng, which has no MathML support. If you modify it to instead include simplewpml-mathmlX.rng (where X is 2 or 3) you get a schema supporting inclusion of MathML version X.

For MathML embedded in SWPX to actually turn into formulas in the Word documents created by the Wordinator you need to supply an XSLT stylesheet that converts MathML to the OOXML math format. (If you don't have such a stylesheet, see the "Finding a stylesheet" section below.)

Running the Wordinator with the stylesheet

The stylesheet must be named MML2OMML.XSL, and will be loaded from classpath.

If you run the Wordinator with -jar wordinator.jar only resources in the jar file will be loaded, and so you will need to build a jar file with the stylesheet included.

Alternatively, you can run the Wordinator as follows:

java -cp wordinator.jar:path/to/directory/with/stylesheet org.wordinator.xml2docx.MakeDocx ...

Running Java this way allows you to point to the directory where the stylesheet is, enabling Java to find it.

Finding a stylesheet

No open source MathML to OOXML stylesheet appears to exist, but one is distributed with Microsoft Office. For copyright reasons it cannot be installed in the Wordinator, but you can extract it from a Microsoft Office installation and make it available to the Wordinator.

You can find this stylesheet in a Windows installation of Microsoft Word at the following location:

C:\Program Files\Microsoft Office\root\Office16\MML2OMML.XSL

You can of course supply any other stylesheet as you wish.

Using MathML in SWPX

Once you've turned on MathML support there are two places where MathML's <mml:math xmlns:mml="http://www.w3.org/1998/Math/MathML"> element can be used in an SWPX file:

  • as a child of <wp:p> for block-level formulae, and
  • as a child of <wp:run> for inline-level formulae.

See the test file test/simplewpml-test-mathml-01.xml for an example of both placements.

Schemas

By default, the simplewpml.rng schema points to a combination of the base SWPX schema and a modified MathML3 schema. It can be changed readily to support a modified MathML2 schema. These MathML RNG schemas were created from the DTDs and then modified specifically for use in Wordinator validation where annotations are not permitted.

Typical MathML annotations are permitted to be restricted and/or augmented by users. These are important constructs in user-facing MathML markup. The MML2OMML.XSL stylesheet does not accommodate annotations and the MathML markup used with SWPX never is user-facing, and so can be deleted from the MathML stream without consequence. The absence of handling annotations in the MML2OMML.XSL stylesheet requires annotations to be deleted.

Support, New Feature Development, and Contributing

The Wordinator project is supported primarily by paying clients who fund development of the features they need. Initial development was funded by Municode.

Please use this project's issue tracker to report bugs or request new features.

I (Eliot Kimber) will attempt to fix bugs as quickly as possible.

For new features, it is unlikely that I will be able to implement them outside of a paid engagement, but if it's something generally usable or something one of my clients needs I may be able to implement it.

If you would like to contribute new features, I welcome all contributions. Use normal GitHub pull requests to submit your contributions. If you'd like to be more heavily involved or even take over primarily development, please contact me directly.

Building

This is a Maven project.

NOTE: POI 4.x and this project require at least Java 8.

Maven dependency:

<dependency>
  <groupId>org.wordinator</groupId>
  <artifactId>wordinator</artifactId>
  <version>1.1.2</version>
</dependency>

Release

To deploy to the public Maven repository use this command line:

mvn clean deploy

(Note: Only the project owner can do this as it requires being able to sign the jar.)

wordinator's People

Contributors

contrext avatar dependabot[bot] avatar drmacro avatar ekimbernow avatar larsga avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

wordinator's Issues

Landscape pages needed

Some tables are landscape tables. For those tables I'd need to be able to define a landscape page. I suppose this could be a section property, but I'm not sure. What would be the simplest way to implement this?

Provide proportional scaling of images

When an image has only one dimension specified explicitly the intrinsic size of the image is used for the other dimension. This can lead to strange results.

When only one dimension is specified explicitly, processor should scale the image proportionately.

Should probably provide the option to turn this behavior off.

Failure on empty colwidth value

Re "Unexpected StringIndexOutOfBoundsException: String index out of range: -2", I've found out what it is. It's due to . I've fixed it by not generating colwidth if not specified. I do get the following warning in this case:

[WARN ] Widths of spanned columns are neither all percents or all measurements, cannot calculate exact spanned width
[WARN ] Widths are "auto", "auto", "auto", "auto", "auto"

It generates the table fine, but you may still want to check and eventually remove the warning in this case.

Run after <fn> truncated

I've got the following structure in an swpx file:

<p style="IASB Normal npara" styleId="IASBNormalnpara"><run>B20</run><run><tab/></run><run>To illustrate a buildโ€‘up approach, assume that Asset A is a contractual right to receive CU800</run><fn><p style="IASB Normal" styleId="IASBNormal"><run>In this IFRS monetary amounts are denominated in โ€˜currency units (CU)โ€™.</run></p></fn><run> in one year (ie there is no timing uncertainty). There is an established market for comparable assets, and information about those assets, including price information, is available. Of those comparable assets:</run></p>

When Word is generated the <run> after the <fn> (i.e. "<run> in one year ...") is dropped and no text is generated. Please check the zip attached to issue (2) and search for <fn>. There are several <fn> elements. I didn't check them all, but the first two definitely exhibit the behavior described.

Support Java 9+

Lastest POI versions support Java 9+. Wordinator should too.

Compiled .jar missing from 1.0.4 release

1.0.3 includes a zipped jar file which can be used without requiring a java compiler.
1.0.4 ommits this

Please could 1.0.4 (or above) include a compiled .jar file?

cell background colour not displaying correctly

What I did:
Created test.html:

<?xml version="1.0" encoding="utf-8"?>
<html xmlns="http://www.w3.org/1999/xhtml">
  <body>
    <table border="1">
      <tbody>
        <tr>
          <td bgcolor="red"><p>text</p></td>
        </tr>
      </tbody>
    </table>
  </body>
</html>

Run
java.exe -jar wordinator_1.0.3/wordinator.jar -i test.html -o out -x wordinator_1.0.3/html2docx/html2docx.xsl -t wordinator_1.0.3\src\test\resources\docx\Test_Template.dotx
Open html and docx files

What I expected to happen:
HTML shows 'text' in a red box
docx shows 'text' in a red box

What actually happened:
HTML shows 'text' in a red box
docx shows 'text' in a white box

image

Index out of bounds exception

My SWPX file passes the schema, yet I get the following:

     [java] java.lang.StringIndexOutOfBoundsException: begin 0, end -2, length 0
     [java] 	at java.base/java.lang.String.checkBoundsBeginEnd(String.java:3319)
     [java] 	at java.base/java.lang.String.substring(String.java:1874)
     [java] 	at org.wordinator.xml2docx.generator.Measurement.toInches(Measurement.java:63)
     [java] 	at org.wordinator.xml2docx.generator.Measurement.toPixels(Measurement.java:27)
     [java] 	at org.wordinator.xml2docx.generator.DocxGenerator.makeImage(DocxGenerator.java:1961)
     [java] 	at org.wordinator.xml2docx.generator.DocxGenerator.makeParagraph(DocxGenerator.java:1313)
     [java] 	at org.wordinator.xml2docx.generator.DocxGenerator.makeParagraph(DocxGenerator.java:1208)
     [java] 	at org.wordinator.xml2docx.generator.DocxGenerator.handleBody(DocxGenerator.java:478)
     [java] 	at org.wordinator.xml2docx.generator.DocxGenerator.constructDoc(DocxGenerator.java:435)
     [java] 	at org.wordinator.xml2docx.generator.DocxGenerator.generate(DocxGenerator.java:417)
     [java] 	at org.wordinator.xml2docx.MakeDocx.handleSingleSwpxDoc(MakeDocx.java:331)
     [java] 	at org.wordinator.xml2docx.MakeDocx.handleDirectory(MakeDocx.java:353)
     [java] 	at org.wordinator.xml2docx.MakeDocx.handleCommandLine(MakeDocx.java:199)
     [java] 	at org.wordinator.xml2docx.MakeDocx.main(MakeDocx.java:86)

File sent separately by email. Reproducible this time!!

Numbering definitions not copied to DOCX

Given a template that defines paragraph styles that use numbering (i.e., bulleted and numbered list items), the numbering definition from the DOTX file is not included in the generated DOCX file.

Symptom is that paragraphs that should have bullets or numbers to do not.

Left margin on a table is needed

It's currently not possible to specify a left margin on a table. If you check a table in paragraph B27 (as attached to issue (4)), you'll see it's left aligned. It should be aligned with the body of the paragraph but there doesn't seem to be a way to achieve this at the moment.

Unable to modify image size in output DOCX

When testing an input HTML containing an image, we are able to produce a DOCX file however the image size constraints are not observed and a large image displays.

Example:
<img src="./images/logo.jpg" width="200" height="120"/>

In the resulting DOCX I would expect to see the logo.jpg file constrained to the size restrictions detailed, h=200px, h=120px, however this is not the case. If you open the HTML file in a browser, you can see that the image dimensions are applied correctly.

Full HTML Example below:

<!DOCTYPE HTML>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <style>
        body {
            font-family: arial;
        }
    </style>
</head>
<body>
<p style="width:300px;float:right">
    <img src="./images/logo.jpg" width="200" height="120"/>
</p>
<br/><br/><br/><br/>
<div style="width:1000px;float:left">
    <h1>Test Document - 2022 Edition</h1>
</div>
<br/><br/>
<table border="1" style="width:60%">
    <tbody>
    <tr>
        <td style="width:30%; background: #D3D3D3"><b>Summary</b></td>
        <td style="width:70%">This is a test to display the structure</td>
    </tr>
    </tbody>
</table>
<br/>
<table border="1" style="width:60%">
    <tbody>
    <tr>
        <td bgcolor="#D3D3D3" style="width:30%"><b>Document ID No:</b></td>
        <td style="width:70%">1234</td>
    </tr>
    <tr>
        <td bgcolor="#D3D3D3"><b>Issue Date:</b></td>
        <td style="width:70%">10/06/2022</td>
    </tr>
    </tbody>
</table>
<br/><br/><br/>
<h3>Table of Contents</h3>
<hr/>
<br/>
<p>1.1 - Level 1 - Page 4</p>
<br/>
<p>1.2 - Level 2 - Page 5</p>
<br/><br/>
<hr/>
<br/><br/>
<h2>2.1 - Level 1 - Example A</h2>
<p>This is some dummy text to take up space. This is some dummy text to take up space. This is some dummy text to take up space. 
This is some dummy text to take up space. This is some dummy text to take up space. This is some dummy text to take up space. This is some dummy text to take up space. 
</p>
<br/><br/><br/>
<h3>2.1.1 2.1.2 - Level 2 - Example A1 Level 2 - Example A2</h3>
<p>This is some dummy text to take up space. This is some dummy text to take up space. This is some dummy text to take up space. 
This is some dummy text to take up space. This is some dummy text to take up space. This is some dummy text to take up space. This is some dummy text to take up space. 
</p>
<br/><br/>
<hr/>
<h2>2.2 - Level 1 - Example B</h2>
<p>This is some dummy text to take up space. This is some dummy text to take up space. This is some dummy text to take up space. 
This is some dummy text to take up space. This is some dummy text to take up space. This is some dummy text to take up space. This is some dummy text to take up space. 
</p>
<br/><br/><br/>
<h3>2.2.1 2.2.2 - Level 2 - Example B1 Level 2 - Example B2</h3>
<p>This is some dummy text to take up space. This is some dummy text to take up space. This is some dummy text to take up space. 
This is some dummy text to take up space. This is some dummy text to take up space. This is some dummy text to take up space. This is some dummy text to take up space. 
</p>
<br/><br/>
<hr/>
<br/><br/><br/><br/><br/>
<table border="1">
    <tbody>
    <tr bgcolor="#D3D3D3">
        <th>Name</th>
        <th>ID</th>
    </tr>
    <tr>
        <td>Level 2 - Example A1</td>
        <td>3544</td>
    </tr>
    <tr>
        <td>Level 2 - Example A2</td>
        <td>8745</td>
    </tr>
    <tr>
        <td>Level 2 - Example B1</td>
        <td>2486</td>
    </tr>
    <tr>
        <td>Level 2 - Example B2</td>
        <td>9745</td>
    </tr>
    </tbody>
</table>
<br/>
</body>
</html>

Nested tables appear in the schema not to be allowed

Reviewing the schema, it seems that tables are allowed only in body content and footnote content.

Body content is allowed only in headers, footers, and the body.

It seems that tables are not allowed in table cells, but such is needed a lot in my data as I have nested tables all over the place. (Maybe I can mitigate many of them, but I don't think all)

Since the recommendation is to validate SWPX before processing it, I've built that into my workflow and so I am stuck.

Example swpx files not applying styles correctly.

What I did:

java.exe -jar ../Tools/wordinator_1.0.3/wordinator.jar -i wordinator_1.0.3\src\test\resources\simplewp\simplewpml-test-02.swpx -o out -t wordinator_1.0.3\docx\Test_Template.dotx

What I expected to happen:

  • docx created with styles from Test_Template.dotx

What actually happened:

  • All heading styles seem to be ignored and just display in standard font within the docx (for example Heading 2 below).

image

I am using wordinator 1.0.3.

Unfortunately this makes the swpx format unusable for me because I can't use styles from the dotx file.

Last section creates unwanted trailing blank page

The last section in a multi-section document results in an extra blank page.

The solution is to put the section definition for the last section directly within the body, not within the last paragraph of the section.

Provide SWPX to DOCX Comparison tool for verifying generated result

Implement within Wordinator a function that compares the input SWPX to the generated DOCX to verify:

  • All content in runs is reflected correctly in the result DOCX (i.e., a run-to-run comparison)
  • All fields are in the SWPX are reflected in the DOCX
  • Headers and footers match

It does not need to handle chunked DOCX results, meaning that it only verifies the SWPX when there is exactly one DOCX result generated from it.

For implementation, one approach would be to use XmlObject to construct cursors on the SWPX and the Document and then walk them in parallel--there should be a one-to-one alignment between paragraphs and runs modulo the effect of hyperlinks and fields, which have a slightly different (simpler) structure in SWPX than in DOCX.

Because there is not an exact alignment between formatting properties in SWPX and DOCX, doing validation on the formatting properties would be more involved and is not an immediate requirement.

The primary purpose of this comparison is to ensure that no content is being dropped or duplicated from the SWPX to DOCX.

Add MathML support

We need MathML support in Wordinator, and are willing to implement it, and make a PR.

If we do we'll add it to the SWPX schema, and use Microsoft's MML2OMML.xsl to transform the MathML into Office math format. We'll then use the POI API to load the transformed XML into org.openxmlformats.schemas.wordprocessingml.x2006.main objects, and then put those into the XWPFDocument.

There may be issues with redistributing the stylesheet as part of Wordinator, so we may need a workaround for that.

Would a PR adding this feature be welcome? Are there any particular issues you'd be concerned about? I'm asking about this before we start to avoid complications down the road.

Can't set styles of table entries

I'm trying to set a style of a table header and table row.
The table style and paragraph style for the table text are both defined in my dotx.

But whatever html I write, I cannot get wordinator to apply a table style from my dotx into the generated docx.

HTML in:

      <table>
        <tbody>
          <tr class="trstyle1">
            <!-- start table row -->
            <th>Heading A</th>
            <th>Heading B </th>
            <th>Heading C </th>
          </tr>
          <tr>
            <!-- start another row -->
            <td>Cell 1A </td>
            <td>Cell 1B </td>
            <td>Cell 1C </td>
          </tr>
          <tr>
            <!-- start another row -->
            <td class="right">Cell 2A </td>
            <td>Cell 2B </td>
            <td>Cell 2C </td>
          </tr>
          <tr>
            <!-- start another row -->
            <td>Cell 3A </td>
            <td>Cell 3B </td>
            <td>Cell 3C </td>
          </tr>
        </tbody>
      </table>

get-style-name.xsl:

  <xsl:variable name="classToStyleNameMap" as="map(xs:string, xs:string)">
    <xsl:map>
      <xsl:map-entry key="'trstyle1'" select="'Table Text'"/>
    </xsl:map>
  </xsl:variable>

Every time I just get a plain table with no styles applied to any of the cells or text.

image

Am I missing something? Maybe someone could share an example where this works? I am using 1.03 and I see that table styles were added at 1.0, but I can't get them to work at all.

Issues with column widths if colspan is used

It looks like column widths are not calculated correctly when there are columns with colspan specified.
I'm attaching a simple swpx file with two tables. The first has a colspan in the first cell, the second is based on this same table but with no colspan (and correspondingly fewer columns). Note that the first table is just a subset of a much larger table, but I've commented all body rows except the first to keep it short.
The second table has the first column of the correct width.
The first table should be the exactly the same but because of colspan the first column (spanned) is much too narrow.
test.swpx.zip

Bug in table cell property values

A causes a crash with the following error:

[java] java.lang.IllegalArgumentException: No enum constant org.apache.poi.xwpf.usermodel.ParagraphAlignment.JUSTIFY
[java]     at java.lang.Enum.valueOf(Enum.java:238)
[java]     at org.apache.poi.xwpf.usermodel.ParagraphAlignment.valueOf(ParagraphAlignment.java:29)
[java]     at org.wordinator.xml2docx.generator.DocxGenerator.makeTableRow(DocxGenerator.java:2045)
[java]     at org.wordinator.xml2docx.generator.DocxGenerator.makeTable(DocxGenerator.java:1552)
[java]     at org.wordinator.xml2docx.generator.DocxGenerator.handleBody(DocxGenerator.java:372)
[java]     at org.wordinator.xml2docx.generator.DocxGenerator.handleSection(DocxGenerator.java:416)
[java]     at org.wordinator.xml2docx.generator.DocxGenerator.handleBody(DocxGenerator.java:369)
[java]     at org.wordinator.xml2docx.generator.DocxGenerator.constructDoc(DocxGenerator.java:339)
[java]     at org.wordinator.xml2docx.generator.DocxGenerator.generate(DocxGenerator.java:312)
[java]     at org.wordinator.xml2docx.MakeDocx.handleSingleSwpxDoc(MakeDocx.java:272)
[java]     at org.wordinator.xml2docx.MakeDocx.handleDirectory(MakeDocx.java:294)
[java]     at org.wordinator.xml2docx.MakeDocx.handleCommandLine(MakeDocx.java:163)
[java]     at org.wordinator.xml2docx.MakeDocx.main(MakeDocx.java:69)
[java] + 2019-08-21 14:39:02,053 [ERROR] Unexpected IllegalArgumentException: No enum constant org.apache.poi.xwpf.usermodel.ParagraphAlignment.JUSTIFY

According to the rng "justify" is a valid value for align attribute on a . There must be a constants mismatch of some kind.

The swpx snippet that causes the crash is like this:

  <td colspan="3" align="justify" borderstylebottom="single" borderstyleright="single">
       <p style="IASB Table Arial" styleId="IASBTableArial">
            <run italic="true">(To recognise the foreign exchange gain on the bond, the adjustment to its carrying amount measured at fair value in LC and the movement in the accumulated impairment amount due to changes in foreign exchange rates)</run>
       </p>
  </td>

Ability to include attachments in DOCX output with URL or Base64 encoded file

At present, images appear to need to be stored at the same location/server from which the Wordinator is running from, i.e. src="file:/Users/ekimber/workspace/wordinator/src/test/resources/html/images/picture-of-something.jpg" when they are referenced.

It is not possible to specify either:

  1. An absolute URL, i.e. src="https://upload.wikimedia.org/wikipedia/commons/thumb/2/2f/Google_2015_logo.svg/1200px-Google_2015_logo.svg.png"
  2. The base-64 encoded contents of the file itself, i.e. src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASAB..." to have used in the DOCX output.

Please could the support of both of these attachment provision methods (URL and base64 encoded file) be looked at for inclusion in the Wordinator tool.

Table sizings are not adhered to following implementation of #70

When testing the change made for #70 , images are now successfully constrained to the detailed size however I've noticed that tables now seem to be suffering from a lack of proportions following the implementation of this change.

It can be seen in the image-geometry-test files that you uploaded whereby each column of the table has one character per line. Tables in this file are specified using % widths, is there perhaps a conflict between using px values for images and % values for tables now?

image-geometry-test.docx (1).zip

Table option "Automatically resize to fit contents" should be disabled by default.

Alternatively, there should be an option to disable it. Without it the column widths are ignored in some cases, especially in east Asian languages which tend to have long strings without spaces. I'm attaching a sample swpx and Word files where this is apparent. Search for "The following warnings and precautions apply to:". You'll see column widths are 1.0 and 1.67 respectively. But this is ignored due to the content of the cell in the second column with "P305+P351 +P338" in the first column.

Table rows have lots of space above and below

I'm not sure this is a Wordinator issue, maybe it can be somehow fixed with a different template. But in case it's worth a check.

I'm attaching a docx file generated from the swpx attached to issue (2).

The TOC on page 1 is generated as a table. Note how far apart the text in rows is. Have no idea how to make the rows less high.

If you check the table on page 30, it's even worse. It has three rows ("ILLUSTRATIVE EXAMPLES", "APPENDIX", "Amendments to the guidance on other IFRSs") which are far apart.

Any suggestion as how this may be fixed is welcomed (either in swpx or in dotx).
bv-ifrs13.docx

Allow turn on debug output from the command line.

A common problem (at least for me) while trying to use wordinator is styles being ignored and dropping to the default style (Body text/Normal) for the document when converting from html to docx.

This is hard to debug - it's not clear where the problem is occurring.

I notice that baseProcessing.xsl contains lots of $doDebug statements which appear to assist debugging this.

But I can't find any way of turning them on.

Please could this be enabled from the command line or similar mechanism? E.g. a command line switch or extra option.

Even if I put in some xsl:messagetest</xsl:message> statements in baseProcessing.java, these don't seem to display anything, so I'm not sure where the problem is.

Generate working ToC, not just ToC field

It should be possible to generate the ToC entries so that on open Word prompts to update the ToC.

Jarno's OOXML plug-in for Open Toolkit generates all the ToC entries and when you open it Word prompts you to update the page numbers or the full ToC.

Math support

From what I see Wordinator cant implement math transformations. I couldn't find any other information about (SWPX) online.
Would it be possible to implement math transforations, how hard it would be (optimistic hope).
Thank you for your work!

Implement own exception type for reporting errors?

At the moment errors are reported to the user like this:

+ 2023-01-27 11:59:54,997 [ERROR] RuntimeException: RuntimeException: -x (transform) parameter not specified. If the input is a _Book.xml file, you must specify the -x parameter

It's not really great that user errors are presented as log messages instead of going to stderr and being clearly labeled as user-friendly error messages the user needs to deal with. The RuntimeException: RuntimeException: is also not great.

A good mechanism for letting the detail Java code report errors up to the MakeDocx CLI driver would be to define a WordinatorException for wordinator errors, and throw that in cases like the error above. MakeDocx would then be able to report these nicely to the user, and also to correctly stop processing (with shell error codes etc).

If this sounds like a good idea I'll be happy to make a PR and start converting at least some of the code to this method.

Page breaks don't work

I've come across a few issues related to page breaks. It looks like page breaks are not implemented at all although I can see them available in rng file.

I've tried the following.

<section type="nextPage"> (I've also tried type="oddPage", just to test).
<p ... page-break-before="true">.
<p ...><break type="page"/>... .

Neither works and the last is not allowed at all as Wordinator doesn't accept a inside a

even though it's allowed in the rng.

I'm attaching an swpx file with lots of sections with page breaks.
bv-ifrs13.zip

Comments

Hello, Eliot:

Our customer has prioritized another functionality that I would like to consult you on: comments. I saw in the release notes that this is something that was targeted for a future release but I don't know if it was completed. Are comments something that was completed or that is being worked on? Thank you very much ...

John

Errors log4j running form command line

Hello I tried to build run wordinator in my ubuntu18, managed to build and run the jar from command line.

The output shows something related to the LogManager.. am i missing something?

thank you in advance,

$ java -jar wordinator.jar -i html/sample_web_page.html -o out -x xsl/html2docx/html2docx.xsl -t docx/Test_Template.dotx
WARNING: sun.reflect.Reflection.getCallerClass is not supported. This will impact performance.
+ 2019-12-02 18:37:23,047 [INFO ] Input document or directory='html/sample_web_page.html'
+ 2019-12-02 18:37:23,049 [INFO ] Output directory           ='out'
+ 2019-12-02 18:37:23,049 [INFO ] DOTX template              ='docx/Test_Template.dotx'
+ 2019-12-02 18:37:23,050 [INFO ] XSLT template              ='xsl/html2docx/html2docx.xsl'
+ 2019-12-02 18:37:23,050 [INFO ] Chunk level                ='root'
Exception in thread "main" java.lang.ExceptionInInitializerError
	at org.wordinator.xml2docx.MakeDocx.transformXml(MakeDocx.java:212)
	at org.wordinator.xml2docx.MakeDocx.handleCommandLine(MakeDocx.java:168)
	at org.wordinator.xml2docx.MakeDocx.main(MakeDocx.java:69)
Caused by: java.lang.UnsupportedOperationException: No class provided, and an appropriate one cannot be found.
	at org.apache.logging.log4j.LogManager.callerClass(LogManager.java:555)
	at org.apache.logging.log4j.LogManager.getLogger(LogManager.java:580)
	at org.apache.logging.log4j.LogManager.getLogger(LogManager.java:567)
	at org.wordinator.xml2docx.generator.DocxGeneratingOutputUriResolver.<clinit>(DocxGeneratingOutputUriResolver.java:27)
	... 3 more

XSLT from HTML to SWPX for list paragraphs with bullet or numbers

Hi Eliot,
refered to fixed Issue 15, do you also have the XSL Transformation from HTML to SWPX for list paragraphs with bullets and numbers?

I saw the code of your example in html2docx/get-style-name.xls, but couldn't adapt it working for bullets and numbers.

Thanks in advance,
Steffi

Enable setting table cell border color

The OOXML markup allows @color to be set on each border of table cell. The SimpleWP markup and DOCX generation should support that.

Need to document (or refer to) the Word border conflict resolution rules and possibly provide a way to set the strategy of OOXML provides options.

Missing Enum for JUSTIFY

A causes a crash with the following error:

[java] java.lang.IllegalArgumentException: No enum constant org.apache.poi.xwpf.usermodel.ParagraphAlignment.JUSTIFY
[java] at java.lang.Enum.valueOf(Enum.java:238)
[java] at org.apache.poi.xwpf.usermodel.ParagraphAlignment.valueOf(ParagraphAlignment.java:29)
[java] at org.wordinator.xml2docx.generator.DocxGenerator.makeTableRow(DocxGenerator.java:2045)
[java] at org.wordinator.xml2docx.generator.DocxGenerator.makeTable(DocxGenerator.java:1552)
[java] at org.wordinator.xml2docx.generator.DocxGenerator.handleBody(DocxGenerator.java:372)
[java] at org.wordinator.xml2docx.generator.DocxGenerator.handleSection(DocxGenerator.java:416)
[java] at org.wordinator.xml2docx.generator.DocxGenerator.handleBody(DocxGenerator.java:369)
[java] at org.wordinator.xml2docx.generator.DocxGenerator.constructDoc(DocxGenerator.java:339)
[java] at org.wordinator.xml2docx.generator.DocxGenerator.generate(DocxGenerator.java:312)
[java] at org.wordinator.xml2docx.MakeDocx.handleSingleSwpxDoc(MakeDocx.java:272)
[java] at org.wordinator.xml2docx.MakeDocx.handleDirectory(MakeDocx.java:294)
[java] at org.wordinator.xml2docx.MakeDocx.handleCommandLine(MakeDocx.java:163)
[java] at org.wordinator.xml2docx.MakeDocx.main(MakeDocx.java:69)
[java] + 2019-08-21 14:39:02,053 [ERROR] Unexpected IllegalArgumentException: No enum constant org.apache.poi.xwpf.usermodel.ParagraphAlignment.JUSTIFY

According to the rng "justify" is a valid value for align attribute on a . There must be a constants mismatch of some kind.

Explicitly-Specified Footnote Callouts

Provide the ability to specify, for an individual footnote, the callout to use for the referencer in the source and, optionally, the reference in the footnote list (by default they would be the same but Word allows them to be different).

Word does not provide a way to have separate numbering streams for footnotes of a given type (i.e., bottom-of-page footnotes where different notes reflect different numbering streams).

In addition, there is sometimes a need to have a footnote that is not numbered, i.e., a "*" footnote used along with numbered footnotes (although in that case usually any numbered footnotes are end notes but it's not a requirement).

Thus, there needs to be a way to give footnotes arbitrary callouts.

In the Word markup, the solution is:

  • Set the callout text in both the footnoteReference (in content) and footnoteRef (in footnote)
  • Set the @customMarkFollows attribute on the footnoteReference and generate the mark (<w:t>) after the footnoteRef element
  • Remove the footnoteref element from the footnote.
  • Set the mark text in the footnote, by default using the in-content callout.

This will require constructing the OOXML directly as the WPXFFootnote element does not provide for custom marks.

Feature request: image data from embedded Base64

My XHTML that I am transforming into SWPX embeds all graphics as follows:

<img src="data:image/png;base64,iVBORw0KGgoAAAANSUh...XXa7Z"/>

There is no resolvable URI with which an image file can be dereferenced using the http: scheme.

It would be handy if wp:image would support the data: scheme in the src= attribute. No file management necessary. The reason I have it in my HTML is so that the HTML is a single monolithic file and not an unmanageable collection of graphics.

I would simply copy my HTML src= attribute into my SWPX src= attribute and that job would be done.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.