First, a quick description of the end-goal:
I'm building a cross-platform, .NET Core-based, printing app. This app will be able to print all sorts of file types with custom page settings, such as headers, footers, and margins. A key feature is it supports multiple-pages-up (e.g. a landscape sheet of paper with two portrait pages rendered side by side...called "2up").
Printing HTML is important not just because of printing HTML, but I want to use all the great HTML-based syntax-highlighting out there for source code (e.g. www.prismjs.com).
The app is basically done but for one major problem: I can't get the HTML to render well enough. So far I've implemented source code printing three ways:
1) As plain text with my own line-numbering and line-wrap engine. This works wonderfully for everything I can throw at it, but it does not support syntax highlighting.
2) Using Html-Renderer (https://github.com/ArthurHub/HTML-Renderer/issues) an OSS .NET-based Html Renderer. This implementation is the weakest because Html-Renderer's CSS support is really weak. It can't handle hardly anything prismjs or highlightjs' generate.
3) Usinglitehtml' (www.litehtml.com) via LiteHtmlSharp. This was very promising and I almost have it working with some major hacks, but litehtml also does not support key, modern, HTML/CSS features.
Neither Html-Renderer or litehtml support the CSS page-break-before feature that, when combined with media print would let me ensure lines are not split between pages.
What I really want to use is the Chromium rendering engine. litehtml provides a fantastic API for this sort of problem: It calls me whenever it needs to render, and I draw (text, table borders, images, etc...) using GDI+. My dream is to find something in Chromium (CEF, Puppeteer, ???) that provides a similar API.
Or, an alternative, an API that will let me pass in a GDI+ Graphics (or HDC) and the renderer will render to that surface.
With Html-Renderer I measure calculate # pages like this.
SizeF size = HtmlRender.MeasureGdiPlus(g, html, containingSheet.GetPageWidth());
int numPages = (int)(size.Height / containingSheet.GetPageHeight());
My page rendering code (e.g. OnPaint) looks like this:
SizeF size = new SizeF(containingSheet.GetPageWidth(), containingSheet.GetPageHeight());
HtmlRender.RenderGdiPlus(g, html, PointF(0, 0), size );
With htmllite the OnPaint code looks like this:
// Set the clip such that any extraLines are clipped off bottom
g.SetClip(new Rectangle(0, 0, (int)Math.Round(PageSize.Width), (int)Math.Round(PageSize.Height - remainingPartialLineHeight)));
LiteHtmlSize size = new LiteHtmlSize(Math.Round(PageSize.Width), Math.Ceiling(PageSize.Height));
litehtml.Document.Draw((int)-0, (int)-yPos, new position {
x = 0,
y = 0,
width = (int)size.Width,
height = (int)size.Height
});
And in this case the call to litehtml.Document.Draw causes a bunch of callbacks into my app that I process using the same Graphics the OnPaint is called with.
Most discussions of CEF and Chromium point to ScreenshotAsync etc... which will not do because I need to be rendering to a PRINTERS HDC (or GDI+) and blitting bitmaps will loose quality.
I have poured over the Chromium source and I cannot find the obvious way to say to CEF/Chromium "render page 1 (defined as Height/Width) to this GDI+ Graphics object" then "render page 2..." etc... The printing support (and how pdfium is integrated come close!).
Chromium issue 311308 indicates I'm hosed until this work gets picked up again.
Note: I have full access to nodejs w/in my app. I have built a dotnet/nodejs bridge, which is how I convert the raw text file of a source code file to richly formatted, line-numbered, syntax-highlighted html via prismjs. This means I could easily use puppeteer/Headless Chrome if I could just figure out the right APIs.
Does anyone have a suggestion that might help? I'm willing to contribute to Chromium if it's not major heart surgery.
Related
How it is reproduced:
I go to the page where the viewer is used. Let's say, while waiting for it to fully load (it doesn't matter), there is no error in the console. Next, I go to another page on the site where the viewer is used.
When I load the page, an error appears in the console(attach a screenshot). After that, if I try to draw the model in the viewer, the viewer does not draw the model completely. And the viewer does not allow you to work with elements (for example, selection).
As a result of what could this error appear?
I didn't do anything special with the initialization of the viewer
Viewer verssion - 7.65
It is definitely possible to run the viewer in multiple browsers/tabs at the same time. Each tab should be using its own GPU context. I just tried loading the following URLs in different browsers at the same time, and all the viewers are rendering the models correctly:
https://forge-basic-app.herokuapp.com/#dXJuOmFkc2sub2JqZWN0czpvcy5vYmplY3Q6Zm9yZ2UtYmFzaWMtYXBwLWJ1Y2tldC9yYWNfYWR2YW5jZWRfc2FtcGxlX3Byb2plY3QucnZ0
https://forge-basic-app.herokuapp.com/#dXJuOmFkc2sub2JqZWN0czpvcy5vYmplY3Q6Zm9yZ2UtYmFzaWMtYXBwLWJ1Y2tldC9TcG9ydHMlMjBDYXIuZHdmeA
Are you seeing this issue with any Forge app (like the one I linked to), or just with your own one? And have you tried other browsers and other devices? It could also be hardware-related. Perhaps your hardware has fewer resources, and when one of the browser tabs loses context, the viewer cannot recover from that?
Several weeks ago I have been asked to upgrade a web application based on a very old version of MXGraph library (version 2.4). The application integrated also the 'grapheditor' a sort of demo application evolved later in Diagramly
and then in Draw.io). Recently I completed the more problematic step, the transition from old "grapheditor" to Draw.io, so I am now able to open all the previous diagrams (saved as plain XML), modify and save them consistently.
Ok, this is the nice part. The bad side is the 'read-only' section of the application ,where the users can more or less, only view the graph.
This page is based on the mxClient.js that renders the graph described in the xml through this code:
var graph = new mxGraph(container);
var diagram = mxUtils.parseXml(xml);
var codec = new mxCodec(diagram);
codec.decode(diagram.documentElement, graph.getModel());
graph.fit();
Upgrading the MX library to the last version (3.9.10) the same code works but some shapes are not rendered properly, they appears as squares instead of
circles, ellipses, etc. The two following images are an example of this misbehavior
Graph in the draw.io:
Same graph rendered by mxClient:
After some tries I discovered that the old mxClient is able to render the same graph perfectly (as draw.io does) so I think there have to be something wrong (or missing) in my code or mxGraph installation/configuration.
As a temporary workaround I can keep in place the old version of mxGraph but obviously I'd like to use the new one.
Can someone give me an hint on this? Any help would be very appreciated.
The tape shape isn't part of core mxGraph, it's part of the GraphEditor example, in the additional shapes JavaScript.
If you look at the style of the ellipse, it's probably not the one in the core, most likely another one from Shapes.js.
Either pull in shapes.js, or use the viewer in draw.io.
I feel noob on this one... But here it comes:
Let's take a Vector program with a PS exporter (with no font sub setting: important to change text dynamically in the future) more specifically the Inkscape version 0.46.
Document size A4 and lets draw some lines very close to the border and a simple text, after that you export your ps as noborder.ps:
Everything is really fine! What is on the first lines of postscript file?
%!PS-Adobe-3.0
%%Creator: 0.46
%%Pages: 1
%%Orientation: Portrait
%%BoundingBox: 0 0 596 842
%%HiResBoundingBox: 0 0 596 842
%%DocumentMedia: plain 596 842 0 () ()
%%EndComments
%%Page: 1 1
Now we need to generate a PS file from this PS file (Why? Some new fonts that cannot be uploaded to the printer and dynamically text are changed. PS2PS is a good choice to embed fonts and other elements prior to printing). Let's use ps2ps from ghostscript 8.7.
user#server:/$ ps2ps noborder.ps whyborder.ps
Very good! No errors on running... But... What? BORDERS? MARGINS? CROPPING?
Lets look at the whyborder.ps header:
%!PS-Adobe-3.0
%%Pages: (atend)
%%BoundingBox: 5 6 587 792
%%HiResBoundingBox: 5.000000 6.791406 586.732813 792.000000
%.........................................
%%Creator: GPL Ghostscript 870 (pswrite)
%%CreationDate: 2015/09/09 16:09:24
%%DocumentData: Clean7Bit
%%LanguageLevel: 2
%%EndComments
%%BeginProlog
% This copyright applies to everything between here and the %%EndProlog:
Why is the BoundingBox changed??? Why add borders, margins, cropping?
I have tested options like "-dEPScrop", papersize... But the cropping remains... Why???
Firstly, stop using an ancient version of Ghostscript! 8.70 is now 6 years old, the current version is 9.16 (shortly to be superceded by 9.18).
Secondly, when experimenting like this do not use a script, use the command line directly. The device being used in the archaic version of GHostscript was pswrite, which was a very poor implementation and only supported level 1 output. The current code uses the ps2write device which is a much more powerful and flexible solution.
Note that in all cases running the input through Ghostscript does not 'embed fonts' or 'edit' or 'compress' the original. What happens is that your input is interpreted to produce graphics primitives which are fed to the device API, the device in question then processes the primitives. For a rendering device this means calling the graphics library to render the primitive to the canvas. For a high level device it means re-emitting the primitive , for example as a PDF operation.
ANY such processing brings inherent risks of approximation, the pswrite device was even worse in that much of the content was rendered to images. So in general it really doesn't embed new fonts, it just embeds pictures of the glyphs. This scales really badly and because the bounding box depends on the resolution can result in signficant inaccuracies.
You should really avoid doing ths unless there is no alternative. If you really must do it, be prepared to accept compromises, do not use archaic versions of Ghostscript and don't use the crappy old pswrite device.
Thank you KenS;
By upgrading the ghostscript to version 9.16 and using the following command everything was fine:
user#server:/$ ps2ps -sPAPERSIZE=a4 noborder.ps whyborder916.ps
This is a old and stable system (PHP/Bash/Ghostscript/CUPS), used for many years as a factory labeling system with old postscript printers also. More recently there was the need to change font style; ps2ps was the best choice to "embed" the glyphs and barcodes (postcript language) that dynamically change with the production line and packaging without need to change the printers (different models from different manufacturers). Since PostScript is a language with few changes in years, never crossed my mind to change the GS version.
At this point this is a real money saver! Thanks again!
I have a Windows Store (Metro) application. I need to add support for scanning barcodes.
I tried using ZXing first. From what I was able to get working, you actually need to click and save an image for it to do the processing. There's no nice overlay of a red line "scanner" nor does it process a live feed. This isn't a very elegant solution. It works far better on Android. Basically, this won't work as I need a constant video and a constant search for a barcode to be in focus.
This blog (http://www.soulier.ch/?p=1275&lang=en) mentions that extrapolating a frame out of a WinRT video stream is not allowed in managed code which means I'd need to use C++.
So, are there any components out there that do this? Anything free or paid that I can get that would be written in C++ and can find and extrapolate a barcode? Learning C++ is not on my bucket list.
You can capture frames while displaying a preview with C# only. Here's an example control that does it:
https://winrtxamltoolkit.codeplex.com/SourceControl/latest#WinRTXamlToolkit/Controls/CameraCaptureControl/CameraCaptureControl.cs
Basically you need to create a MediaCapture object and associate it with a CaptureElement control to display the preview. Then you can use CapturePhotoToStreamAsync() to capture a frame to a stream of your selected encoding format and then have a go at it with your bar code reading code.
I made a lib for WinRT using ZXing & Imaging SDK.
It works well (but does not include any additional focus feature).
There is a lib and a sample app that you can try.
It works for barcodes and QRCode (barcode by default but just change the optional parameter in the scan function code to use QRCode)
Please bear in mind that I have never used Web Workers before and I'm having some trouble wrapping my head around them.
Here's an explanation of a simplified version of what I'm doing.
My page has links to various files - some are text, some are images, etc. Each file has an image showing a generic file icon.
I want the script to replace each generic icon with a preview of the file's contents.
The script will request the file from the server (thereby adding it to the cache, like a preloader), then create a canvas and draw the preview onto it (a thumbnail for images, an excerpt of text for text files, a more specific icon for media files...) and finally replace the generic icon's source with the canvas using a data URL.
I can do this quite easily. However, I would prefer to have it in the background so that it doesn't interfere with the UI while it's working.
Before I dive right in to this, I need to know: can Workers work with a canvas, and if so how would I create one? I don't think document.createElement('canvas') would work because Workers can't access the DOM, or am I misunderstanding when all the references I've found say they "can't access the DOM"?
You cannot access the DOM from web workers. You cannot load images. You cannot create canvas elements and draw to them from web workers. For now, web workers are pretty much limited to doing ajax calls and doing compute intensive things. See this related question/answer on web workers and canvas objects: Web Workers and Canvas and this article about using webworkers to speed up image manipulation: http://blogs.msdn.com/b/eternalcoding/archive/2012/09/20/using-web-workers-to-improve-performance-of-image-manipulation.aspx
Your simplest bet is to chunk your work into small chunks (without web workers) and do a chunk at a time, do a setTimeout(), then process the next chunk of work. This will allow the UI to be responsive while still getting your work done. If there is any CPU consuming computation to be done (like doing image analysis), this can be farmed out to a web worker and the result can be sent via message back to the main thread to be put into the DOM, but if not, then just do your work in smaller chunks to keep the UI alive.
Parts of the tasks like loading images, fetching data from servers, etc... can also be done asynchronously so it won't interfere with the responsiveness of the UI anyway if done properly.
Here's the general idea of chunking:
function doMyWork() {
// state variables
// initialize state
var x, y, z;
function doChunk() {
// do a chunk of work
// updating state variables to represent how much you've done or what remains
if (more work to do) {
// schedule the next chunk
setTimeout(doChunk, 1);
}
}
// start the whole process
doChunk();
}
Another (frustrating) limitation of Web Workers is that it can't access geolocation on Chrome.
Just my two cents.
So as others have stated, you cannot access the DOM, or do any manipulations on the DOM from a web worker. However, you can outsource some of the more complete calculations on the web worker. Then once you get your return message from the web worker inside of your main JS thread, you can extract the values you need and use them on the DOM there.
This may be unrelated to your question, but you mentioned canvas so i'll share this with you.
if you need to improve the performance of drawling to canvas, I highly recommend having two canvas objects. One that's rendered to the UI, the other hidden. That way you can build everything on the hidden canvas, then draw the hidden canvas on the displayed one. It may not sound like it will do much if anything, but it will increase performance significantly.
See this link for more details about improving canvas performance.