You are viewing limited content. For full access, please sign in.

Question

Question

Creating Image of Text File via Workflow

asked on April 14, 2022

Got an interesting one.

I have a workflow that is creating an entry in the repository and populating text into the entry from a Token.  This is working wonderfully.

However, I have staff complaining because it's text and not an image (stupid complaint, but whatever).

Does anyone have any suggestions on ways to have my script create an image (just a standard TIFF) of the text in addition to adding the actual text?

Here's the code I'm currently running in my script to add the text into the entry: 

1using (DocumentInfo doc = Document.GetDocumentInfo((int)GetTokenValue("CreateEntry_OutputEntry_ID"), this.RASession))
2{
3    doc.AppendPage();
4    PageInfo PI = doc.GetPageInfo(1);
5    PI.WriteTextPagePart(GetTokenValue("Report Text").ToString());
6    PI.Save();
7}

Thank you!

0 0

Answer

SELECTED ANSWER
replied on April 14, 2022 Show version history

You could use the System.Drawing library to create a TIFF image; the library includes text options so you'd just need to work out the right font sizes and positioning.

Once you have a TIFF file created in a stream, you can write that to the document.

I don't have a process that does exactly what you're doing, but I do have bits from other processes that could give you a general idea of the different steps.

I usually target an image size of 2550x3000, which is 8.5″x11″ at 300DPI

Here's an example of code we use to generate stamp images dynamically (you'd want to save it to ImageFormat.Tiff instead of ImageFormat.Bmp so there's some encoder stuff you may want too).

01//Create test image at runtime
02Bitmap bmp = new Bitmap(width, height);
03using (Graphics g = Graphics.FromImage(bmp))
04{
05// Set string format
06StringFormat stringFormat = new StringFormat();
07 
08// Set horizontal and vertical alignment
09// Note this is the alignment within the drawing rectangle, not overall page alignment
10stringFormat.Alignment = StringAlignment.Center;
11stringFormat.LineAlignment = StringAlignment.Center;
12 
13// Set font rendering and formatting
14g.TextRenderingHint = TextRenderingHint.AntiAlias;
15System.Drawing.Font line1 = new System.Drawing.Font("Arial", 60, FontStyle.Bold, GraphicsUnit.Point);
16System.Drawing.Font line2 = new System.Drawing.Font("Arial", 30, FontStyle.Bold, GraphicsUnit.Point);
17System.Drawing.Font line3 = new System.Drawing.Font("Arial", 25, FontStyle.Bold, GraphicsUnit.Point);
18 
19// clear background
20g.Clear(Color.White);
21 
22// draw image content
23g.DrawString(type.ToUpper(), line1, Brushes.Black, new Rectangle(0, 0, width, 150), stringFormat);
24g.DrawString(vtoken, line2, Brushes.Black, new Rectangle(0, 165, width, 150), stringFormat);
25g.DrawString(subtext, line3, Brushes.Black, new Rectangle(0, 245, width, 150), stringFormat);
26}
27 
28//Convert bitmap to monochrome
29BitmapData bmpData = bmp.LockBits(new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format1bppIndexed);
30bmp = new Bitmap(width, height, bmpData.Stride, System.Drawing.Imaging.PixelFormat.Format1bppIndexed, bmpData.Scan0);
31 
32// copy image to memory stream
33MemoryStream ms = new MemoryStream();
34bmp.Save(ms, ImageFormat.Tiff);
35bmp.Dispose();

 

Here's some code I use to draw a TIFF back to PagePart.Image (we use this in a process that auto-resizes documents that have unnecessarily high dimensions or DPI).

01// Move to beginning of memory stream
02ms.Seek(0, SeekOrigin.Begin);
03 
04// Create write stream
05using (Stream writer = page.WritePagePart(PagePart.Image, (int)ms.Length)){
06    byte[] buffer = new byte[0x8000];
07    int count;
08    while((count = ms.Read(buffer, 0, buffer.Length)) > 0){
09        writer.Write(buffer, 0, count);
10    }
11}

 

I think ImageFormat.Tiff defaults to LZW or uncompressed, but if you want to force it to use something different, like monochrome for smaller file sizes, then you want something like this:

1EncoderParameters encoderParameters = new EncoderParameters(2);
2encoderParameters.Param[0] = new EncoderParameter(Encoder.Compression,compressionType[compressionIndex]);
3encoderParameters.Param[1] = new EncoderParameter(Encoder.Quality,100L);
4 
5// Retrieve encoder
6ImageCodecInfo tiffEncoder = GetEncoderInfo("image/tiff");
7 
8// Save with existing encoders
9newImg.Save(ms,tiffEncoder,encoderParameters);

 

The compression type arrays it references are defined like so

01string[] compressionTypeName = new string[5] {
02        "no compression",
03        "CCITT Group 3",
04        "Facsimile - compatible CCITT Group 3",
05        "CCITT Group 4(T.6)",
06        "LZW"
07};
08long[] compressionType = new long[5] {
09        (long)EncoderValue.CompressionNone,
10        (long)EncoderValue.CompressionCCITT3,
11        (long)EncoderValue.CompressionCCITT3,
12        (long)EncoderValue.CompressionCCITT4,
13        (long)EncoderValue.CompressionLZW
14};

 

And the GetEncoderInfo method is as follows

01private static ImageCodecInfo GetEncoderInfo(string mimeType){
02// Identify and set tiff encoder
03ImageCodecInfo[] encoders = ImageCodecInfo.GetImageEncoders();
04for (int i = 0; i < encoders.Length; i++)
05{
06    if (encoders[i].MimeType == mimeType)
07    {
08        return encoders[i];
09    }
10}
11return null;
12}

 

However, in your case, you could probably set that with static values and would probably want Group 4 rather than LZW unless you want/need color.

I only needed dynamic since I'm resizing existing images and the source image codecs can vary.

3 0
replied on April 14, 2022

Wow!  This is great info.  Thank you @████████

It'll take me a bit to play with all this and try stuff out.

As a side-note - you mentioned that you use the one part of the code to resize documents.  I'd love to hear more about how that works - like how you identify documents that need to be resized - and whether you do things like changing from color to grayscale to reduce size, etc.

0 0
replied on April 14, 2022

Hi Matthew,

When I say "resize" I should probably say rescale because I just mean pixel dimensions and DPI, not specifically targeting image file size.

Although that does affect file size, I'm not using code to change from color to grayscale because color detection is very tricky business.

Basically all it does is check for DPI above 300 and/or documents that are outside of the expected 8.5x11 dimensions.

If an issue is found, it redraws the image.

1 0
replied on April 14, 2022

Oh cool, that's neat.

0 0
replied on April 14, 2022

@████████- Thank you so much for your help.  I've got it working to add the image with the text like I'd like.  I'm very happy with that.

The only hiccup I'm having is it is setting the DPI to 96 instead of 300, and I'm not certain how to tweak that.  Any ideas?

Here's the full script I'm using currently: 

01namespace WorkflowActivity.Scripting.SDKScript
02{
03    using System;
04    using System.IO;
05    using System.Collections.Generic;
06    using System.ComponentModel;
07    using System.Data;
08    using System.Data.SqlClient;
09    using System.Text;
10    using Laserfiche.RepositoryAccess;
11    using System.Drawing;
12    using System.Drawing.Imaging;
13    using System.Runtime.InteropServices;
14 
15    /// <summary>
16    /// Provides one or more methods that can be run when the workflow scripting activity is performed.
17    /// </summary>
18    public class Script1 : RAScriptClass110
19    {
20        /// <summary>
21        /// This method is run when the activity is performed.
22        /// </summary>
23        protected override void Execute()
24        {
25 
26            using (DocumentInfo doc = Document.GetDocumentInfo((int)GetTokenValue("CreateEntry_OutputEntry_ID"), this.RASession))
27            {
28                //Add the token text to the entry.
29                doc.AppendPage();
30                PageInfo PI = doc.GetPageInfo(1);
31                PI.WriteTextPagePart(GetTokenValue("Report Text").ToString());
32                PI.Save();
33 
34                //Variables for image
35                var pageWidth = 2550;  // 8.5" at 300 DPI
36                var pageHeight = 3000; //11.0" at 300 DPI
37 
38                //Create bitmap image
39                Bitmap bmp = new Bitmap(pageWidth, pageHeight);
40 
41                //Add the text to the bitmap image
42                using (Graphics g = Graphics.FromImage(bmp))
43                {
44                    // Set string format
45                    StringFormat stringFormat = new StringFormat();
46 
47                    // Set horizontal and vertical alignment
48                    // Note this is the alignment within the drawing rectangle, not overall page alignment
49                    stringFormat.Alignment = StringAlignment.Near;
50                    stringFormat.LineAlignment = StringAlignment.Near;
51 
52                    // Set font formatting
53                    System.Drawing.Font textFont = new System.Drawing.Font("Arial", 24, FontStyle.Bold, GraphicsUnit.Point);
54 
55                    // clear background
56                    g.Clear(Color.White);
57 
58                    // draw image content
59                    g.DrawString(GetTokenValue("Report Text").ToString(), textFont, Brushes.Black, new Rectangle(50, 50, pageWidth - 100, pageHeight - 100), stringFormat);
60                }
61 
62                //Convert bitmap to monochrome
63                BitmapData bmpData = bmp.LockBits(new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format1bppIndexed);
64                bmp = new Bitmap(pageWidth, pageHeight, bmpData.Stride, System.Drawing.Imaging.PixelFormat.Format1bppIndexed, bmpData.Scan0);
65 
66                //Copy image to memory stream
67                MemoryStream ms = new MemoryStream();
68                bmp.Save(ms, ImageFormat.Tiff);
69                bmp.Dispose();
70 
71                //Move to beginning of memory stream
72                ms.Seek(0, SeekOrigin.Begin);
73 
74                //Reference page 1 in the document.
75                var page = doc.GetPageInfo(1);
76 
77                //Add the image to the page
78                using (Stream writer = page.WritePagePart(PagePart.Image, (int)ms.Length)){
79                    byte[] buffer = new byte[0x8000];
80                    int count;
81                    while((count = ms.Read(buffer, 0, buffer.Length)) > 0){
82                        writer.Write(buffer, 0, count);
83                    }
84                }
85            }
86        }
87    }
88}

 

0 0
replied on April 14, 2022 Show version history

Use the following to set resolution.

1bmp.SetResolution(300,300);

Bitmap.SetResolution(Single, Single) Method (System.Drawing) | Microsoft Docs

1 0
replied on April 15, 2022 Show version history

Thank you so much @████████!

That one should have been obvious to me.  Oh well, I appreciate you saving me the trouble of trying to figure it out.

I'm marking your original post as the selected answer, because this wouldn't have been possible without your help and guidance.

But, for anyone reading this post in the future, I'm posting the final code for reference: 

01namespace WorkflowActivity.Scripting.SDKScript
02{
03    using System;
04    using System.IO;
05    using System.Collections.Generic;
06    using System.ComponentModel;
07    using System.Data;
08    using System.Data.SqlClient;
09    using System.Text;
10    using Laserfiche.RepositoryAccess;
11    using System.Drawing;
12    using System.Drawing.Imaging;
13    using System.Runtime.InteropServices;
14 
15    /// <summary>
16    /// Provides one or more methods that can be run when the workflow scripting activity is performed.
17    /// </summary>
18    public class Script1 : RAScriptClass110
19    {
20        /// <summary>
21        /// This method is run when the activity is performed.
22        /// </summary>
23        protected override void Execute()
24        {
25 
26            using (DocumentInfo doc = Document.GetDocumentInfo((int)GetTokenValue("CreateEntry_OutputEntry_ID"), this.RASession))
27            {
28                //Add the text string from the Workflow token.
29                var textString = GetTokenValue("Report Text").ToString();
30 
31                //Add the token text to the entry.
32                doc.AppendPage();
33                PageInfo PI = doc.GetPageInfo(1);
34                PI.WriteTextPagePart(textString);
35                PI.Save();
36 
37                //Variables for image
38                var imageDPI = 300;
39                var pageWidth = (int)(8.5 * imageDPI);
40                var pageHeight = (int)(11 * imageDPI);
41 
42                //Create bitmap image
43                Bitmap bmp = new Bitmap(pageWidth, pageHeight);
44                bmp.SetResolution(imageDPI,imageDPI);
45 
46                //Add the text to the bitmap image
47                using (Graphics g = Graphics.FromImage(bmp))
48                {
49                    // Set string format
50                    StringFormat stringFormat = new StringFormat();
51 
52                    // Set horizontal and vertical alignment
53                    // Note this is the alignment within the drawing rectangle, not overall page alignment
54                    stringFormat.Alignment = StringAlignment.Near;
55                    stringFormat.LineAlignment = StringAlignment.Near;
56 
57                    // Set font formatting
58                    System.Drawing.Font textFont = new System.Drawing.Font("Arial", 14, FontStyle.Bold, GraphicsUnit.Point);
59 
60                    // clear background
61                    g.Clear(Color.White);
62 
63                    // draw image content
64                    g.DrawString(textString, textFont, Brushes.Black, new Rectangle(50, 50, pageWidth - 100, pageHeight - 100), stringFormat);
65                }
66 
67                //Convert bitmap to monochrome
68                BitmapData bmpData = bmp.LockBits(new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format1bppIndexed);
69                bmp = new Bitmap(pageWidth, pageHeight, bmpData.Stride, System.Drawing.Imaging.PixelFormat.Format1bppIndexed, bmpData.Scan0);
70                bmp.SetResolution(imageDPI,imageDPI);
71 
72                //Copy image to memory stream
73                MemoryStream ms = new MemoryStream();
74                bmp.Save(ms, ImageFormat.Tiff);
75                bmp.Dispose();
76 
77                //Move to beginning of memory stream
78                ms.Seek(0, SeekOrigin.Begin);
79 
80                //Reference page 1 in the document.
81                PageInfo page = doc.GetPageInfo(1);
82 
83                //Add the image to the page
84                using (Stream writer = page.WritePagePart(PagePart.Image, (int)ms.Length)){
85                    byte[] buffer = new byte[0x8000];
86                    int count;
87                    while((count = ms.Read(buffer, 0, buffer.Length)) > 0){
88                        writer.Write(buffer, 0, count);
89                    }
90                }
91            }
92        }
93    }
94}
1 0

Replies

replied on April 14, 2022 Show version history

I want to chime in on this for anybody scared of the code. No offence, guys, this is great stuff!

An no code (or low code) alternative would be to use the Update Word Document activity to send the text string into a Word file, either as a mail merge or simple find & replace. This would give a bit more control over the look of the final document.

In the advanced settings of the Word file activity, change the result to be a PDF. This could then be attached to a newly created LF document or the source document if a doc starts the workflow.

Finally, if you want to go all the way to TIF, calling DCC in version 11 can convert the PDF to TIF, though you'll need to call an extra workflow on completion to remove the PDF layer (if that's a concern).

2 0
replied on April 15, 2022

Thank you @████████

For my particular use case, the code works best for me, but you are right that a no code option is great when possible, and I appreciate you for commenting for anyone reviewing this post in the future.

0 0
replied on April 15, 2022

Great point Pieter. This is a very good no code/low code option.

0 0
You are not allowed to follow up in this post.

Sign in to reply to this post.