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
- either print the document using “Print to PDF”
Word_with_DisplayBarcode_printAs.pdf (371.8 KB) - or “save (the document) as PDF”
Word_with_DisplayBarcode_saveAs.pdf (62.3 KB)
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:
- Our implementation does not inherit from
DocsExamplesBase
, as we don’t have access to that class. - I’ve set the
const float scale
to1.0f;
instead of your value2.4f;
, because the generated QR-codes in the PDF were much larger than the ones displayed in MS Word. - 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
- Docx 2024-01-15_01-09-23_Word_with_DisplayBarcode_Output.docx (10.8 KB)
- or PDF 2024-01-15_01-09-23_Word_with_DisplayBarcode_Output.pdf (18.4 KB)
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.
- The top left corner of the QR-code in the PDF document does not align with the blue markings.
- 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
to1.0f;
instead of your value2.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