Conversion issue from Aspose.Words document with DisplayBarcode to PDF

Hi,
I’m trying to convert a MS Word document with { DisplayBarcode "https://www.bitworks.net" QR \q 3 \h 2250 } into a PDF document using Aspose.Words (for .NET).
Word_with_DisplayBarcode.docx (17.6 KB)

First of all, I must admit, that I am disappointed, that the Aspose.Words product does NOT automatically render the corresponding QC-code during the generation of the PDF. In contrast, when using MS Word to

the QR-codes are automatically included in the output. And thus I’d expect the same behaviour from Aspose.Words, when saving the Aspose.Words.Document using Aspose.Words.Saving.PdfSaveOptions!

After reading a lot of forum discussions, I stumbled upon these instructions: How to Create BarCode|Aspose.Words for .NET and set the Aspose.Words.Document.FieldOptions.BarcodeGenerator accordingly. Currently I’m testing with this feature using the following code:

public void AsposeWords_DisplayBarCode()
{
    #region Setup

    var inputFileName = @".\Word_with_DisplayBarcode.docx";
    var outputDirectory = @".";

    Aspose.Words.License licWords = new Aspose.Words.License();
    licWords.SetLicense("Aspose.Total.lic");

    // Setting the Aspose.PDF.License probably is not necessary, as this does not change the output at all!
    Aspose.Pdf.License licPdf = new Aspose.Pdf.License();
    licPdf.SetLicense("Aspose.Total.lic");

    // Setting the Aspose.Barcode.License probably is not necessary, as this does not change the output at all!
    Aspose.BarCode.License licBarCode = new Aspose.BarCode.License();
    licBarCode.SetLicense("Aspose.Total.lic");

    #endregion Setup

    #region Test

    var now = DateTime.Now;
    foreach (var flagSaveAsPDF in new bool[] { false, true })
    {
        // Load a docx file
        var wordDocument = new Aspose.Words.Document(inputFileName);
        wordDocument.FieldOptions.FieldUpdateCultureSource = FieldUpdateCultureSource.FieldCode;
        wordDocument.FieldOptions.BarcodeGenerator = new Aspose_Words_BarcodeGenerator();

        // Save the corresponding Aspose.Words.Document - as docx or pdf:
        var outputFileName = Path.Combine(outputDirectory, $"{now.ToString("yyyy-MM-dd_hh-mm-ss")}_Word_with_DisplayBarcode_Output.{(flagSaveAsPDF ? "pdf" : "docx")}");
        if (!flagSaveAsPDF)
        {
            // Save as docx:
            // The resulting docx-file contains the image of the QR-code and thus works as expected!
            wordDocument.Save(outputFileName, Aspose.Words.SaveFormat.Docx);
        }
        else
        {
            // Save as PDF:
            // The resulting pdf file does not include the QR-code and displays an error message instead: "Error! Bar code generator is not set."
            var pdfSaveOptions = new Aspose.Words.Saving.PdfSaveOptions
            {
                // None of these options have any notable effect regarding the QR-code:
                EmbedFullFonts = wordDocument.FontInfos.EmbedTrueTypeFonts,
                //UpdateFields = true,
                //UpdateSdtContent = true,
            };
            wordDocument.Save(outputFileName, pdfSaveOptions);
        }
    }

    #endregion Test
}

/// <summary>
/// Source: https://docs.aspose.com/words/net/how-to-generate-a-custom-barcode-image-for-displaybarcode-field/
/// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-.NET.git.
/// </summary>
public class Aspose_Words_BarcodeGenerator : IBarcodeGenerator
{
    /// <summary>
    /// Converts barcode image height from Word units to Aspose.BarCode units.
    /// </summary>
    /// <param name="heightInTwipsString"></param>
    /// <returns></returns>
    private static float ConvertSymbolHeight(string heightInTwipsString)
    {
        // Input value is in 1/1440 inches (twips).
        int heightInTwips = TryParseInt(heightInTwipsString);

        if (heightInTwips == int.MinValue)
            throw new Exception("Error! Incorrect height - " + heightInTwipsString + ".");

        // Convert to mm.
        return (float)(heightInTwips * 25.4 / 1440);
    }

    /// <summary>
    /// Converts barcode image color from Word to Aspose.BarCode.
    /// </summary>
    /// <param name="inputColor"></param>
    /// <returns></returns>
    private static Color ConvertColor(string inputColor)
    {
        // Input should be from "0x000000" to "0xFFFFFF".
        int color = TryParseHex(inputColor.Replace("0x", ""));

        if (color == int.MinValue)
            throw new Exception("Error! Incorrect color - " + inputColor + ".");

        return Color.FromArgb(color >> 16, (color & 0xFF00) >> 8, color & 0xFF);

        // Backward conversion -
        // return string.Format("0x{0,6:X6}", mControl.ForeColor.ToArgb() & 0xFFFFFF);
    }

    /// <summary>
    /// Converts bar code scaling factor from percent to float.
    /// </summary>
    /// <param name="scalingFactor"></param>
    /// <returns></returns>
    private static float ConvertScalingFactor(string scalingFactor)
    {
        bool isParsed = false;
        int percent = TryParseInt(scalingFactor);

        if (percent != int.MinValue && percent >= 10 && percent <= 10000)
            isParsed = true;

        if (!isParsed)
            throw new Exception("Error! Incorrect scaling factor - " + scalingFactor + ".");

        return percent / 100.0f;
    }

    /// <summary>
    /// Implementation of the GetBarCodeImage() method for IBarCodeGenerator interface.
    /// </summary>
    /// <param name="parameters"></param>
    /// <returns></returns>
    public Image GetBarcodeImage(Aspose.Words.Fields.BarcodeParameters parameters)
    {
        if (parameters.BarcodeType == null || parameters.BarcodeValue == null)
            return null;

        BarcodeGenerator generator = new BarcodeGenerator(EncodeTypes.QR);

        string type = parameters.BarcodeType.ToUpper();

        switch (type)
        {
            case "QR":
                generator = new BarcodeGenerator(EncodeTypes.QR);
                break;
            case "CODE128":
                generator = new BarcodeGenerator(EncodeTypes.Code128);
                break;
            case "CODE39":
                generator = new BarcodeGenerator(EncodeTypes.Code39Standard);
                break;
            case "EAN8":
                generator = new BarcodeGenerator(EncodeTypes.EAN8);
                break;
            case "EAN13":
                generator = new BarcodeGenerator(EncodeTypes.EAN13);
                break;
            case "UPCA":
                generator = new BarcodeGenerator(EncodeTypes.UPCA);
                break;
            case "UPCE":
                generator = new BarcodeGenerator(EncodeTypes.UPCE);
                break;
            case "ITF14":
                generator = new BarcodeGenerator(EncodeTypes.ITF14);
                break;
            case "CASE":
                generator = new BarcodeGenerator(EncodeTypes.None);
                break;
        }

        if (generator.BarcodeType.Equals(EncodeTypes.None))
            return null;

        generator.CodeText = parameters.BarcodeValue;

        if (generator.BarcodeType.Equals(EncodeTypes.QR))
            generator.Parameters.Barcode.CodeTextParameters.TwoDDisplayText = parameters.BarcodeValue;

        if (parameters.ForegroundColor != null)
            generator.Parameters.Barcode.BarColor = ConvertColor(parameters.ForegroundColor);

        if (parameters.BackgroundColor != null)
            generator.Parameters.BackColor = ConvertColor(parameters.BackgroundColor);

        if (parameters.SymbolHeight != null)
        {
            generator.Parameters.ImageHeight.Pixels = ConvertSymbolHeight(parameters.SymbolHeight);
            generator.Parameters.AutoSizeMode = AutoSizeMode.None;
        }

        generator.Parameters.Barcode.CodeTextParameters.Location = CodeLocation.None;

        if (parameters.DisplayText)
            generator.Parameters.Barcode.CodeTextParameters.Location = CodeLocation.Below;

        generator.Parameters.CaptionAbove.Text = "";

        // Empiric scaling factor for converting Word barcode to Aspose.BarCode.
        const float scale = 1.0f; //2.4f;
        float xdim = 1.0f;

        if (generator.BarcodeType.Equals(EncodeTypes.QR))
        {
            generator.Parameters.AutoSizeMode = AutoSizeMode.Nearest;
            generator.Parameters.ImageWidth.Inches *= scale;
            generator.Parameters.ImageHeight.Inches = generator.Parameters.ImageWidth.Inches;
            xdim = generator.Parameters.ImageHeight.Inches / 25;
            generator.Parameters.Barcode.XDimension.Inches =
                generator.Parameters.Barcode.BarHeight.Inches = xdim;
        }

        if (parameters.ScalingFactor != null)
        {
            float scalingFactor = ConvertScalingFactor(parameters.ScalingFactor);
            generator.Parameters.ImageHeight.Inches *= scalingFactor;

            if (generator.BarcodeType.Equals(EncodeTypes.QR))
            {
                generator.Parameters.ImageWidth.Inches = generator.Parameters.ImageHeight.Inches;
                generator.Parameters.Barcode.XDimension.Inches =
                    generator.Parameters.Barcode.BarHeight.Inches = xdim * scalingFactor;
            }

            generator.Parameters.AutoSizeMode = AutoSizeMode.None;
        }

        //#if NET48 || JAVA
        return generator.GenerateBarCodeImage();
        //#elif NET6_0 || __MOBILE__
        //generator.GenerateBarCodeImage().Save(ArtifactsDir + "GetBarcodeImage.png");
        //return Image.Decode(ArtifactsDir + "GetBarcodeImage.png");
        //#endif
    }

    /// <summary>
    /// Implementation of the GetOldBarcodeImage() method for IBarCodeGenerator interface.
    /// </summary>
    /// <param name="parameters"></param>
    /// <returns></returns>
    public Image GetOldBarcodeImage(Aspose.Words.Fields.BarcodeParameters parameters)
    {
        if (parameters.PostalAddress == null)
            return null;

        BarcodeGenerator generator = new BarcodeGenerator(EncodeTypes.Postnet)
        {
            CodeText = parameters.PostalAddress
        };

        //#if NET48 || JAVA
        return generator.GenerateBarCodeImage();
        //#elif NET6_0 || __MOBILE__
        //generator.GenerateBarCodeImage().Save(ArtifactsDir + "OldBarcodeImage.png");            
        //return Image.Decode(ArtifactsDir + "OldBarcodeImage.png");
        //#endif
    }

    /// <summary>
    /// Parses an integer using the invariant culture. Returns Int.MinValue if cannot parse.
    /// 
    /// Allows leading sign.
    /// Allows leading and trailing spaces.
    /// </summary>
    public static int TryParseInt(string s)
    {
        return double.TryParse(s, NumberStyles.Integer, CultureInfo.InvariantCulture, out double temp)
            ? CastDoubleToInt(temp)
            : int.MinValue;
    }

    /// <summary>
    /// Casts a double to int32 in a way that uint32 are "correctly" casted too (they become negative numbers).
    /// </summary>
    public static int CastDoubleToInt(double value)
    {
        long temp = (long)value;
        return (int)temp;
    }

    /// <summary>
    /// Try parses a hex String into an integer value.
    /// on error return int.MinValue
    /// </summary>
    public static int TryParseHex(string s)
    {
        return int.TryParse(s, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out int result)
            ? result
            : int.MinValue;
    }
}

Here the Aspose_Words_BarcodeGenerator is basically a copy of your Github gist Aspose.Words for .NET. Generate сustom BarCode using C#. · GitHub with minor adjustments:

  1. Our implementation does not inherit from DocsExamplesBase, as we don’t have access to that class.
  2. I’ve set the const float scale to 1.0f; instead of your value 2.4f;, because the generated QR-codes in the PDF were much larger than the ones displayed in MS Word.
  3. Because we do not define the symbol NET48, I’ve commented out the respective directives (#if NET48 || JAVA, …).

At this point, when saving the results (of the code) as

at least I’m consistently having the QC-code image in the output.

In the original MS Word document Word_with_DisplayBarcode.docx, I’ve used blue lines (Shapes -> Lines of MS Word) around the QR-code to visualize its outline and thus the remaining alignment issues. Comparing the resulting Docx and PDF files, one can immediately see, that the outline of the QR-code in the Docx file still matches the blue lines, whereas the QC-code in the PDF document does not.

  1. The top left corner of the QR-code in the PDF document does not align with the blue markings.
  2. The QR-code in the PDF document is larger than the one in the docx file. (Although I’ve already set the const float scale to 1.0f; instead of your value 2.4f;, as described earlier!)

Both of these aspects negatively impact the PDF document, because in complex documents the QR-code is going to be misaligned and either overlaps or displaces other content and thus may disrupt the visual output (text alignment around the QR-code image, line breaks, textflow, …) of the document when comparing the docx and the PDF documents.

Is there any way to more precisely align the generated QR-code with the way MS Word renders the QR-code in their docx documents? And I’m not talking about the precise version of the QR-code (i.e. version 2 [25x25] vs. version 3 [29x29]), the mask pattern used to encode the data in the QR-code or anything like those details. It’s just about the size and positioning of the QR-code image in the resulting PDF.

These tests have been conducted using the latest versions of Aspose products (for .NET) at the time:

  • Words: 24.1.0
  • PDF: 23.12.0
  • BarCode: 23.12.0

Kind regards,
Matthias

@M.Heinz
We have opened the following new ticket(s) in our internal issue tracking system and will deliver their fixes according to the terms mentioned in Free Support Policies.

Issue ID(s): WORDSNET-26466

You can obtain Paid Support Services if you need support on a priority basis, along with the direct access to our Paid Support management team.

The issues you have found earlier (filed as WORDSNET-26466) have been fixed in this Aspose.Words for .NET 24.8 update also available on NuGet.

Does this fix also require the August updates for Apsose.PDF and/or Aspose.BarCode? Because just the Aspose.Words update does not seem to fix the issue at all.

P.S.: And the July updates for PDF and BarCode do not seem to suffice to make any changes.

@M.Heinz Please use the updated code from the following article:
https://docs.aspose.com/words/net/how-to-generate-a-custom-barcode-image-for-displaybarcode-field/
or from here:
https://docs.aspose.com/barcode/net/how-to-create-custom-barcodes-with-ibarcodegenerator/

Replacing the Aspose_Words_BarcodeGenerator class is the original code sample (posted on Jan 15th 2024) with the classes CustomBarcodeGenerator and CustomBarcodeGeneratorUtils does not resolve the issue.

As of today, I’m testing with the most recent versions of the Aspose product:

  • Aspose.Words 24.8
  • Aspose.PDF 24.8
  • Aspose.BarCode 24.7

Good behavior:
Reading and then saving the original docx file as a docx file using Aspose does not change the size or position of the generated QR-code; this was previously correct and still looks good to me. (You can compare the attached docx file dated 2024-08-19 with the one dated 2024-01-15 from the post on Jan 15th.)

Misbehavior:
The update you’ve provided does realign/move the QR-code within the resulting PDF file (i.e. the bottom right corner does seem to align with the bottom right corner position in the xlsx file), but the update does not size the QR-code properly; i.e. it still exceeds the bounding box marked in the original docx file. You can see this by comparing the resulting PDF file against the xlsx file and specifically by looking at the top left corner of each QR-code and their alignment with the markers drawn in blue.

The following attachments have been generated using the most current versions of Aspose and the latest version of the CustomBarcodeGenerator class:

Could you please double check on your end using the input file Word_with_DisplayBarcode.docx from the Jan 15th post and make sure, the resulting QR-code is bound by the blue markers?

@M.Heinz To get the correct position on the left/top, you can update part of the code to the following:

// Set size.
if (gen.BarcodeType == EncodeTypes.QR)
{
    gen.Parameters.Barcode.XDimension.Pixels = (float)Math.Max(1.0, Math.Round(CustomBarcodeGeneratorUtils.DefaultQRXDimensionInPixels * scalingFactor));
    gen.Parameters.Barcode.Padding.Left.Pixels = 16;
    gen.Parameters.Barcode.Padding.Top.Pixels = 16;
}
else
    gen.Parameters.Barcode.XDimension.Pixels = (float)Math.Max(1.0, Math.Round(CustomBarcodeGeneratorUtils.Default1DXDimensionInPixels * scalingFactor));

However, we can’t change anything with the image size, at the moment it is slightly different. We will discuss this situation with the Aspose.BarCode team and get back to you with more information.

@amjad.sahi Could you please help us with the founded issues in BarcodeGenerator?

There are several problems:

  1. If AutoSizeMode is not specified by default, then it’s impossible to specify the exact value of the “get.Parameters.Barcode.X Dimension.Pixels = 3.4f” - 3.4 will always be rounded down at the output to 3, and 3.5 will always be 4. How can I specify more accurate values so that by default the picture is displayed with the dimensions as in the MS Word?

  2. If AutoSizeMode = AutoSizeMode.Nearest, then the generated image will differ in large margins. In the first case, when AutoSizeMode = AutoSizeMode.None you need to additionally specify paddings so that the picture gets into the user’s lines. In the case of AutoSizeMode = AutoSizeMode.Nearest does not need to specify them anymore.

Here is an output images:

AutoSizeMode.None:

AutoSizeMode.Nearest:

Here is an input file:
Barcode.docx (17.9 KB)

@vyacheslav.deryushev,

I discussed the matter with the product lead. This is an issue with IBarcodeGenerator architecture. IBarcodeGenerator does not allow to set Resolution and pixels cannot be float.
https://docs.aspose.com/barcode/net/how-to-create-custom-barcodes-with-ibarcodegenerator/

Problem can be fixed with inserting barcode images with high resolution into word or PDF documents manually:
Word: https://docs.aspose.com/barcode/net/add-barcode-to-word-document/#using-cursor-position
PDF: https://docs.aspose.com/barcode/net/add-barcode-to-pdf-document/#standard-way

Moreover, problem can also be fixed with IBarcodeGenerator architecture fix (add Resolution to result structure of public Image GetBarcodeImage(Aspose.Words.Fields.BarcodeParameters parameters)).

Hope, this helps a bit.

1 Like

@amjad.sahi Thanks, the resolution setting helped with the first problem.
And the second case with the difference in indentation between AutoSizeMode.Nearest and AutoSizeMode.None - is this some problem in Aspose.BarCode or is this expected behavior?

Set Barcode Sizing Mode

https://docs.aspose.com/barcode/net/customize-barcode-appearance/#set-barcode-sizing-mode

1 Like

@M.Heinz Please check the answers. CustomBarcodeGenerator provides you the basic idea of using Aspose.BarCode when converting .docx to .pdf. Due to some limitations, you can update the provided code and configure the barcode for your desired purpose. To get the same output as the .docx file, you can update the code as follows:

// Set size.
if (gen.BarcodeType == EncodeTypes.QR)
{
    gen.Parameters.Resolution = 103;
    gen.Parameters.Barcode.XDimension.Pixels = (float)Math.Max(1.0, Math.Round(CustomBarcodeGeneratorUtils.DefaultQRXDimensionInPixels * scalingFactor));
    gen.Parameters.Barcode.Padding.Left.Pixels = 16;
    gen.Parameters.Barcode.Padding.Top.Pixels = 16;
}
else
    gen.Parameters.Barcode.XDimension.Pixels = (float)Math.Max(1.0, Math.Round(CustomBarcodeGeneratorUtils.Default1DXDimensionInPixels * scalingFactor));

To control the size of the barcode, you can change gen.Parameters.Resolution as you need.

Main problem here is that Aspose.Words ignores the resolution of provided image and counts the resolution as 96 dpi, independently from real resolution.

@alexander.gavriluk Ok, we will check it.

1 Like

@vyacheslav.deryushev Thanks for your answer. I’ll definitely try this approach, but I’m still curious, as to why both outputs would render different barcode sizes!

As you can imagine, I’m not simply trying to convert this one document from a docx file into a PDF (-> I could adjust the layout manually to fix any issues for a single document), but we’re using Aspose.Words in our software to provide customers the possibility to convert their documents from docx to PDF. And thus we don’t have any control over the initial docx files, that will be input to the algorithm and we would expect the size and position of the barcodes to always match between the docx and the resulting PDF file, such that the layout of the original docx and the resulting PDF file remains the same.

I can hardly imagine, setting the gen.Parameters.Resolutionto 103 and the top and left offset to 16px each would work for any possible barcode created with a BarCode-MergeField from MS Word.

Thus I’d like to be able to implement a general solution, which does not depend on the specifics of the MergeField and instead works as is for any provided `BarCode-MergeField.

Can you please take a stand on my suspicion, that the provided values (resolution 103 and offset 16,16) will only work in specific cases of QR-BarCode-MergeFields and evaluate a general solution to the issue at hand?

Btw, could you please investigate, as to why I’m not receiving any Email notifications for this topic?

The email-address associated with this account is current and these are my notification settings:

Yeah, as expected, this only works for QR codes version 3. I’ve added an other QR code version (simply by adding more text to the BarCode-MergeField) to the input docx file and - not surprisingly - your presets (especially the gen.Parameters.Resolution of 103) do not work anymore.

You can verify this by using the following input document:
Word_with_DisplayBarcode_2_QR_codes.docx (21.0 KB)

@M.Heinz I’m afraid there is no universal solution that covers all sizes of barcode images generated by MS Word fields. Aspose.Words inserts the barcode image provided by Aspose.BarCode as is. You can get acquainted with the described limitations and possible solutions on the next page:

I’ll create a new task to further investigate and improve the solution, but for now I don’t think there is a one-line code to save all barcode sizes as MS Word created them.

@M.Heinz
We have opened the following new ticket(s) in our internal issue tracking system and will deliver their fixes according to the terms mentioned in Free Support Policies.

Issue ID(s): WORDSNET-27314

You can obtain Paid Support Services if you need support on a priority basis, along with the direct access to our Paid Support management team.

Thanks for investigating this issue further.

You can probably understand, that the previous fix doesn’t cover our use-case, as the BarCode text is a user input and as such may vary in length and thus will the QR code version.

I’m fine with a static offset of 16x16 px from the top left - as long as this offset doesn’t vary depending on the QR code version. Although I don’t see any point in this offset not being applied automatically by Aspose itself, as this clearly aligns the QR code with the positioning used by MS Word.

Thanks again for your dedication and I’m looking forward to any improvements you can provide.