Welcome Guest! You need to login or register to make posts.

Notification

Icon
Error

Options
Go to last post Go to first unread
Nifty  
#1 Posted : Thursday, November 23, 2017 10:26:05 AM(UTC)
Nifty

Rank: Advanced Member

Groups: Member
Joined: 10/12/2015(UTC)
Posts: 44

I don't understand how the IFontSource logic works. I'm using the PsdProcessor to parse and spit out a Jpeg, but it contains fonts that aren't on my computer. That is fine, I want to substitute a different font for the one specified in the psd.

Here is the code that I'm using:

Code:
public class MyFontResolver : IFontSource {

  public string GetFont(string fontName, string cachePath) {

    var target = cachePath + string.Format("\\{0}.ttf", fontName);
    File.Copy(@"D:\Temp\PsdTests\_inputFiles\fonts\1.ttf", target) ;

    return target;

  }

}


I'm then using it like this:
Code:
public byte[] RenderImage() {

  var customResolver = new FontResolver(@"D:\Temp\PsdTests\_inputFiles\Fonts\cache");
  customResolver.AddSource(new MyFontResolver());

  var processor = new Aurigma.GraphicsMill.Templates.PsdProcessor(customResolver);

  using (var memIn = new MemoryStream(_psdBytes))
  using (var memOut = new MemoryStream()) {

    var jpegSettings = new Aurigma.GraphicsMill.Codecs.JpegSettings();
    processor.Render(memIn, memOut, jpegSettings);
                
    return memOut.ToArray();

  }

}


Everything works the first time it runs, although files aren't placed where I would expect them. When my code does the File.Copy method, the "1.ttf" is copied to a temp directory that GraphicsMill creates (like expected), but after the render it appears to be moved to the cache directory that I pass in as a parameter ("D:\Temp\PsdTests\_inputFiles\Fonts\cache"). That's fine (although I do notice that the directory ends up with a ton of extra sub folders created by GraphicsMill).

It works after the first run, but if I run my small little console app again it doesn't work. I get two copies of the file in my cache directory and it throws a FontMissingException. I'm guess that I'm implementing the IFontSource incorrectly, but now sure what is wrong.

Note: The reason that I do the File.Copy in "GetFont" method of MyFontResolver is because if I just return the location of the ttf font, it appears to get moved to the cached directory...which means that the font in our local directory is gone.

Any help would be greatly appreciated.
Eugene Kosmin  
#2 Posted : Sunday, November 26, 2017 9:58:54 PM(UTC)
Eugene Kosmin

Rank: Advanced Member

Groups: Member, Administration, Moderator
Joined: 9/19/2006(UTC)
Posts: 505

Was thanked: 41 time(s) in 41 post(s)
Hi,

From the next version, you will be able to use FontRegistry.FallbackFonts collection for the font substitution.

PsdProcessor will take the first fallback font for unknown font creation.

Here is a sample:
Code:
var customResolver = new FontResolver(fontCache);
customResolver.FontRegistry.FallbackFonts.Add("Arial");
customResolver.AddSource(new FileSystemSource(fontCache));

var processor = new PsdProcessor(customResolver);
processor.Render(in, out);


In general, IFontSource is needed for obtaining fonts from external services like google fonts. There is only one method with font name and cache path arguments. It means you need to find the certain font by name and save it to the path with write permissions.

You can also you FileSystemSource for getting fonts from local storages. Or write your own as you did. I would recommend creating the first source from the cache path. It should speed up further runnings.
Best regards,
Eugene Kosmin
The Aurigma Development Team
Nifty  
#3 Posted : Monday, November 27, 2017 9:28:58 AM(UTC)
Nifty

Rank: Advanced Member

Groups: Member
Joined: 10/12/2015(UTC)
Posts: 44

I'm still getting a "FontMissingException" the second time I run my application. The StackTrace looks like this:

Code:
   at Aurigma.GraphicsMill.AdvancedDrawing.FontRegistry.CreateFont(String postscriptName, Single size, Single dpiX, Single dpiY)
   at Aurigma.GraphicsMill.Templates.PsdFrameExtensions.ConvertToText(PsdTextFrame textFrame, FontRegistry fontRegistry)
   at Aurigma.GraphicsMill.Templates.PsdProcessor.CreateText(PsdTextFrame psdTextFrame)
   at Aurigma.GraphicsMill.Templates.PsdProcessor.RenderText(PsdTextFrame frame)
   at Aurigma.GraphicsMill.Templates.PsdProcessor.RenderFrame(PsdFrame frame)
   at Aurigma.GraphicsMill.Templates.PsdProcessor.CreateContainer(PsdFrame frame)
   at Aurigma.GraphicsMill.Templates.PsdProcessor.CreateContainer(Stream psdStream)
   at Aurigma.GraphicsMill.Templates.PsdProcessor.Render(Stream psdStream, Stream outStream, WriterSettings writerSettings)


Again, the first run works good, but the second run doesn't. If I delete the ttf file that it copies, the code will run again.
Could it have something to do with the fact that the ttf file doesn't have the same postscript name as the one specified in the psd?

Maybe I'm going about this incorrectly, I'd like to use my own ttf files regardless of what is specified in the psd. I might be asking the wrong question, but it's confusing because it does work the first time I run the application using the code originally posted (MyFontResolver).
Eugene Kosmin  
#4 Posted : Monday, November 27, 2017 5:37:15 PM(UTC)
Eugene Kosmin

Rank: Advanced Member

Groups: Member, Administration, Moderator
Joined: 9/19/2006(UTC)
Posts: 505

Was thanked: 41 time(s) in 41 post(s)
You cannot use IFontSource for the font substitution. While rendering, FontRegistry tries to find the desired font, and if no success, it also compares filenames as a last resort. That's why it works for the first time.

When you run the app for the second time, your font file is already in the font cache directory, so PsdProcessor stores it with another name like font_1.ttf. So FontRegistry will not find it anymore.

There is no check for postscript names of font files got from IFontSource for the sake of performance.

At the moment, the only proper way is using FontFallback collection as I described earlier.
Best regards,
Eugene Kosmin
The Aurigma Development Team
Nifty  
#5 Posted : Tuesday, November 28, 2017 5:38:21 AM(UTC)
Nifty

Rank: Advanced Member

Groups: Member
Joined: 10/12/2015(UTC)
Posts: 44

Thanks for the explanation. I planned on using similar code to your example here https://www.graphicsmill.com/docs/gm/loading-text-layers.htm to loop through their uploaded psd and list the fonts that aren't installed on our system.

Is there a way to update the PsdTextFrame.Font.Name property so we can map any missing fonts? It looks like FontFallbackCollection is a Collection<string>, is there a way to provide a ttf file or a custom FontRegistry class to use?
Nifty  
#6 Posted : Tuesday, November 28, 2017 8:59:19 AM(UTC)
Nifty

Rank: Advanced Member

Groups: Member
Joined: 10/12/2015(UTC)
Posts: 44

Update: I'm now attempting to use the TextCallback to process the frame using my own font, I don't get the MissingFont expection and it's using the correct font, but the size/path/location of the text is off.

Should I continue down this path trying to make things work?

Code:
public byte[] RenderImageWithFonts() {

    var processor = new Aurigma.GraphicsMill.Templates.PsdProcessor();
    processor = new PsdProcessor();

    using (var memIn = new MemoryStream(_psdBytes))
    using (var reader = new PsdReader(memIn))
    using (var gc = new GraphicsContainer(reader.Width, reader.Height, reader.DpiX, reader.DpiY))
    using (var graphics = gc.GetGraphics())
    using (var customFont = new CustomFontRegistry()) {

        customFont.Add(@"D:\Temp\PsdTests\_inputFiles\fonts\1.ttf");
        graphics.FontRegistry = customFont;

        using (var memOut = new MemoryStream()) {

            processor.TextCallback = (p, textFrame, textString) => {

                var fhope = graphics.CreateFont(customFont.Fonts[0].PostscriptName, textFrame.FontSize);
                var plainText = new PlainText(textString, fhope, new System.Drawing.PointF(textFrame.TextBox.X, textFrame.TextBox.Y));

                return plainText;

            };

            processor.Render(reader, gc);

            using (var image = new ImageGenerator(gc, PixelFormat.Format32bppArgb, RgbColor.Transparent))
            using (var writer = new PngWriter(memOut)) {

                Pipeline.Run(image + writer);

                return memOut.ToArray();

            }

        }

    }

}
Eugene Kosmin  
#7 Posted : Tuesday, November 28, 2017 4:46:57 PM(UTC)
Eugene Kosmin

Rank: Advanced Member

Groups: Member, Administration, Moderator
Joined: 9/19/2006(UTC)
Posts: 505

Was thanked: 41 time(s) in 41 post(s)
FontFallbackCollection contains font postscript names, which uniquely identifies the fonts you need. There is a list of fonts because while rich text processing, for example, some glyph could be absent in the first fallback font, and the next one will be checked in this case.

CustomFontRegistry returns the postscript name of the font when you add it. So, you can use it instead of TTF file.

PSD text frame has a lot of properties, and it is difficult to create a text object taking into account all of them. That's why we have created PsdProcessor. It contains this logic, and you need to use it.

Code:
processor.TextCallback = (p, textFrame, textString) =>
{
    // Create a text object with PsdProcessor
    var t = p.ProcessText(textFrame);

    // And replace the font with your favorite
    t.Font = graphics.CreateFont("Arial", t.Font.Size);

    return t;
};
Best regards,
Eugene Kosmin
The Aurigma Development Team
Users browsing this topic
Forum Jump  
You cannot post new topics in this forum.
You cannot reply to topics in this forum.
You cannot delete your posts in this forum.
You cannot edit your posts in this forum.
You cannot create polls in this forum.
You cannot vote in polls in this forum.