Gecko Engine in ABCPDF not finding tags - gecko

Has anyone tried to implement tags using the ABCPDF Gecko engine? I have it working fine on the MSHTML engine (Internet Explorer) as soon as I use Gecko, which is rendering my HTML better, it can't find the tags specified in the HTML.
I'm using style="abcpdf-tag-visible: true;" to specify a tag which works using the default engine.
The following code produces a blank document.
[Test]
public void Tags_With_Gecko()
{
Doc theDoc = new Doc();
theDoc.Rect.Inset(100, 100);
theDoc.Rect.Top = 700;
theDoc.HtmlOptions.Engine = EngineType.Gecko;
// Tag elements with style 'abcpdf-tag-visible: true'
theDoc.HtmlOptions.ForGecko.AddTags = true;
int id = theDoc.AddImageHtml("<FONT id=\"p1\" style=\"abcpdf-tag-visible: true; font-size: 72pt\">Gallia est omnis divisa in partes tres.</FONT>");
// Frame location of the tagged element
XRect[] tagRects = theDoc.HtmlOptions.ForGecko.GetTagRects(id);
foreach (XRect theRect in tagRects)
{
theDoc.Rect.String = theRect.ToString();
theDoc.FrameRect();
}
// Output tag ID
string[] tagIds = theDoc.HtmlOptions.ForGecko.GetTagIDs(id);
theDoc.Rect.String = theDoc.MediaBox.String;
theDoc.Rect.Inset(20, 20);
theDoc.FontSize = 64;
theDoc.Color.String = "255 0 0";
theDoc.AddText("Tag ID \"" + tagIds[0] + "\":");
// Save the document
const string testFilename = #"C:\pdf\HtmlOptionsGetTagRects.pdf";
if (File.Exists(testFilename))
File.Delete(testFilename);
theDoc.Save(testFilename);
theDoc.Clear();
Process.Start(testFilename);
}
Almost identical code for the default engine produces it correctly.

I've been talking to WebSuperGoo support. Found out the documentation isn't consistent/complete.
http://www.websupergoo.com/helppdfnet/source/5-abcpdf/xhtmloptions/2-properties/addtags.htm
In Gecko, your tag has to have a visible impact on the page for it to be picked up. In my case, I had a tag that displayed a non-breaking space, and thus it wasn't found.
From their example, changing the style to the following got it to be findable:
style="abcpdf-tag-visible: true; border: 1px solid transparent"
Note the Border settings is what makes this work apparently.
Again, this fixes their demo and thus should fix Dillorscroft's example.
I have to futz a bit more to fix my problem, as I am trying to allocate blank spaces on the page (for a table of contents) so they can be updated after the html is rendered and I know where the first content page will start.

Related

Strange offset with Watir/Capybara and PhantomJS

I want to detect the location of elements on a page using Watir and PhantomJS.
My second approach using Capybara resulted in the same offset.
While the elements on the left side look good, the right side is misaligned:
I made the screenshot before and after I grab the positions for each element with element.wd.location, but the offset is always the same. I used evaluate_script and .getBoundingClientRect() with Capybara.
One thing looks suspicious to me: The search input field is not loaded correctly and not only shows a misalignment, but also a different size and the magnifying glass isn't shown. I don't know if this causes the offset.
I tested it with pure PhantomJS 2.1.1 (phantomjs file.js):
var fs = require('fs');
var page = require('webpage').create();
page.viewportSize = {
width: 1024,
height: 768
};
page.open('http://en.wikipedia.org/', function() {
var positions = page.evaluate(function() {
positions = [];
elements = document.getElementsByTagName('IMG');
for (var i=0, l=elements.length; i<l; i++) {
pos = elements[i].getBoundingClientRect();
positions.push(pos.left + ' ' + pos.top);
};
return positions;
});
fs.write('test.txt', positions.join("\r\n"), 'w');
page.render('test.png');
phantom.exit();
});
Same result: If you open the test.png, you see the an image on the right (left: 952px, top: 259px), but the test.txt shows it shifted to the left (left: 891px).
Do you know what could cause this problem?
Do you know what could cause this offset?
A bug in PhantomJS v2.1.1 or in the embedded Qt WebEngine.
Is there any workaround ?
No.
But I want it to work anyway, how?
Fix it yourself or hire someone to fix it or wait for it to be fixed.
Note that the issue no longer occurs in version 2.5, but it is still in beta :
https://github.com/ariya/phantomjs/milestone/16
https://bitbucket.org/ariya/phantomjs/downloads/
Here's a screenshot taken with phantomjs-2.5.0-beta :
This seems to be an issue in PhantomJS.
On the GitHub thread of the issue, #dantarion seems to have found a solution:
I am running this as well.
My fix is to run the following on the page in an evaluate block to force PhantomJS to render at the right height viewport. It works for my use case, and while I want to see it fixed in 2.2, since its still an issue I thought I'd post here.
document.getElementsByTagName("body")[0].style.overflow = "hidden";
document.getElementsByTagName("body")[0].style.height = "1080px";
document.getElementsByTagName("body")[0].style.maxHeight = "1080px";
document.getElementsByTagName("html")[0].style.overflow = "hidden";
document.getElementsByTagName("html")[0].style.height = "1080px";
document.getElementsByTagName("html")[0].style.maxHeight = "1080px";
It seems to be solving the problem. The only problem being that background-size: cover might still be off (as reported by #Luke-SF).

Are these 2 capabilities supported in Safari extension development?

I've written a Chrome/Opera extension and am considering porting it to Safari. It needs 2 specific capabilities and the absence of either would veto the whole plan.
Ability to read HTTPS urls - Chrome supports this as part of the "tabs" permission. Firefox (last I checked) does not.
Ability to dynamically change the appearance of the activation button. - In Chrome, this is achieved by having a single canvas on the background page (i.e. the button)...
-body- -canvas id="button_canvas" width="19" height="19"- -/canvas- -/body-
... and then dynamically changing it whenever necessary ...
var canvas = document.getElementById("button_canvas");
var context = canvas.getContext("2d");
var imageData = context.getImageData(0, 0, 19, 19);
// write stuff to the canvas
context.putImageData(imageData, 0, 0);
imageData = context.getImageData(0, 0, 19, 19);
// key element below
chrome.browserAction.setIcon({
imageData: imageData
});
If anyone can answer these two questions definitively before I buy a used Mac on Craigslist, I'd appreciate it. Thanks!
p.s. FWIW, my own Googling suggests that #2 is not possible in Safari. No idea about #1.
I believe both are possible.
For HTTPS URLs: In the Safari extension builder, under Extension Website Access, set Access Level to All and tick the Include Secure Pages checkbox.
To dynamically change the icon displayed on a toolbar icon, first set any icon in the Safari extension builder. Then if you want to change in response to a toolbar button click:
safari.application.addEventListener('command', performCommand, false);
function performCommand(event) {
if (event.command === 'changeIcon') {
event.target.image = safari.extension.baseURI+'othericon.png';
}
}
Under other circumstances, you can iterate your toolbar buttons to modify the one you want:
var toolbarButtons = safari.extension.toolbarItems;
for (var i = 0; i < itemArray.length; ++i) {
var item = toolbarButtons[i];
if (item.identifier === "mybutton") {
item.image = safari.extension.baseURI+'othericon.png';
}
}

Printing PDF documents from Windows 8 App

I'm trying to print a PDF file from my Windows 8 app to connected printer. I'm coding with WinJS, and know that I have to create a print task to initiate printing from a Windows 8 app. So, after reviewing the documentation, I have this code:
onPrintTaskRequested: function (e) {
var self = Application.navigator.pageControl,
printTask = e.request.createPrintTask("Print Test Doc", function (args) {
args.setSource(MSApp.getHtmlPrintDocumentSource(document));
// Register the handler for print task completion event
printTask.oncompleted = self.onPrintTaskCompleted;
});
}
According to the documentation, the MSApp.getHhtmlPrintDocumentSource method accepts a specific set of data types. As stated in the documentation:
This can be the root document, the document in an IFrame, a document
fragment, or a SVG document. Be aware that htmlDoc must be a document,
not an element.
Apparently I cannot simply set the argument for getHtmlPrintDocumentSource to a .PDF or .PNG binary. So, I'm curious: does the WinJS library offer a method for printing so that I can implement the printing of a PDF file to a connected printer? Can anybody offer some tips to implement?
After trial and error, I was finally able implement the printing of a Base64 stream representing a PDF binary from a Windows 8 application.
I'm coding the app in HTML / CSS / WinJS. Essentially here is a brief explanation of how it was accomplished:
Create a new <canvas> element within the default.html file. Place it right after the open tag of the element. Like this:
<body role="application" class="app">
<canvas id="pdf-render-output"></canvas>
.
.
.
</body>
Then inside the default.css file, setup a few rules as well as a print media query. Like this:
body > canvas {
display: none;
}
.
. /* all your app's default css styles */
.
#media print {
body > * {
display:none;
max-width: 100%;
}
html {
max-width: 100%;
border-top-color: none;
border-top: 0;
}
body > canvas {
display: block;
border: none;
max-width: 100%;
width: 100%;
height: 100%;
position: relative;
}
}
Of note is the order in which the rules are declared in CSS. It's important to place the print media query after declaring default CSS rules.
After this is setup, javascript handles the rest. The basic idea is to render the PDF.js output to the "hidden" canvas in the DOM. When the document object gets sent to print, the CSS print media declaration is queried so that all elements under <body> are hidden except for the canvas element. Here is the javascript to print only the first page in the PDF:
//Define a container for the Base64 data we'll use with PDF.js
var pdfPrintData = {};
//Function to render PDF to canvas and begin printing contract with Windows 8 OS
printPrescription: function () {
var self = Application.navigator.pageControl,
printManager = Windows.Graphics.Printing.PrintManager.getForCurrentView();
self.getPDF().done(function () {
var pdfStream = pdfPrintData.base64,
pdfFile = convertDataURIToBinary(pdfStream);
PDFJS.disableWorker = true;
PDFJS.getDocument(pdfFile).then(function (pdf) {
var numPages = pdf.numPages,
renderCanvas = $('#pdf-render-output')[0];
//setup canvas
renderCanvas.height = pdf.getPage(1).data.getViewport(1).height;
renderCanvas.width = pdf.getPage(1).data.getViewport(1).width;
//Setup a render context for pdf.js to out a pdf file to the canvas.
var renderContext = {
canvasContext: renderCanvas.getContext('2d'),
viewport: pdf.getPage(1).data.getViewport(1)
};
//Bring up Windows 8 OS print after PDF is rendered to render context.
pdf.getPage(1).data.render(renderContext).then(function () {
printManager.onprinttaskrequested = self.onPrintTaskRequested;
Windows.Graphics.Printing.PrintManager.showPrintUIAsync();
});
})
});
},
onPrintTaskRequested: function (e) {
var self = Application.navigator.pageControl,
printTask = e.request.createPrintTask("Print Prescription", function (args) {
args.setSource(MSApp.getHtmlPrintDocumentSource(document));
printTask.oncompleted = self.onPrintTaskCompleted;
});
},
onPrintTaskCompleted: function (e) {
if (e.completion === Windows.Graphics.Printing.PrintTaskCompletion.failed) {
console.log("[ERX] : Failed to print!");
}
}
The self.getPDF method is just a function that retrieves the Base64 data stream, and that streams gets set on the .base64 property of the global pdfPrintData object. For some reason, I was not able to render the pdf using pdf.js to a dynamically create canvas in a dynamically created document. I had to render the output of the pdf.js render method to a canvas already present in the DOM.
As far as I know, MSApp.getHtmlPrintDocumentSource(document) is meant to be used with HTML document objects, and nothing else.
If you can assume Windows 8.1, you can try to assemble a new HTML document from your PDF file by exporting each page into a raster image using PdfPage.RenderToStreamAsync. There is a sample project in MSDN for a PDF viewer that uses this new API where you can learn how to use this method.
If you cannot assume Windows 8.1 and you need to support plain Windows 8 or Windows RT (ARM), you might need to use a third party library to create the raster images or to do the printing all together.
Amyuni PDF Creator for WinRT for example can do the printing for you. Disclaimer: I currently work for the company that develops the library

TCPDF with FPDI templates and THEAD

I have a html table with THEAD output with writeHTML which should be displayed on every page at the top of the table. It works but when I use a template loaded by FPDI the head is white and disappears on the second page and further. I can mark the head field with the mouse but the appear white. The border only appears as a little point on the left.
I already tried to add $this->setPageMark() to the addPage method. But its still the same issue.
public function AddPage($orientation = '', $format = ''){
parent::AddPage($orientation, $format);
if($this->template != null){
$this->useTemplate($this->template);
$this->setPageMark();
}
}
you need to enable page breaks at the beginning which is causing the issue you are having;
this is a sample code snippet ;
require_once('tcpdf/tcpdf.php'); //main code
require_once('tcpdf/fpdi.php'); //read existing pdf and sends to fpdf
$pdf = new FPDI();
$pdf->setPrintHeader(false); //no header
$pdf->setPrintFooter(false);//no footer
$pdf->SetAutoPageBreak(FALSE, 0); // set auto page breaks
//loop starts here
{
$pdf->setSourceFile($page_background); //set page
$templateId = $pdf->importPage(1); //we only need the first page
$pdf->useTemplate($templateId); //use the imported page
//your write html code and any other tcpdf related code comes here
}
$pdf->Output(outcome.pdf, 'I'); //use F instead of I to show generated pdf.

Justify text in SQL Reporting Services

Is there a way of fully-justifying text in SQL Reporting Services?
I've been searching around and it seems the feature is still not supported by Reporting Services, but are there any workarounds?
I know this question has been asked before, but maybe progress has been made in the mean time.
This is not possible, at least not in SSRS 2008 and below. The only options for aligning text are Left, Center and Right.
The only workaround I could think of was enabling HTML tags in a text box, but the styling for Justify alignment is just ignored. So there really aren't any suitable workarounds AFAIK, short of using picture with justified text (~shudder!~).
You should keep an eye on the corresponding MS feedback item and perhaps vote on it as well. It used to have 527 votes, but was reset to 0 during the move from MS Connect to this new feedback site. I found the bug report through this social.msdn thread, which has been going on for quite some time.
'picture with justified text in SSRS': you can create a AdvRichTextBox control (see code http://geekswithblogs.net/pvidler/archive/2003/10/14/182.aspx ) and use it in ssrs following these steps : http://binaryworld.net/Main/CodeDetail.aspx?CodeId=4049
Here's a possible workaround : Full Text Just
It makes use of RS utility and OLE Automation to do the job.
In Standard, SSRS does not Support justify. There are possibilities to work around:
Use a third party control doing this: (I was not able to get one to work.)
Call a component via COM like Word. (Is a security issue, but possible.)
Format the box in HTML and put small white spaces between the words. This can be done in a stored procedure.
The solution 3 is very long to describe in detail. This is the reason why I put my solution for free download on my web page.
The advantage of my solution is, that there is no installation necessary.
Here is the link to my solution: http://www.rupert-spaeth.de/justify/
If you use <p> try with:
$("[style*='padding-bottom:10pt']").css("text-align", "justify");
The following will work if you open the .rdl code file (which is xml).
You need a paragraph tag, if it doesn't already exist.
This formats a number to use commas (U.S. style) with two points after the decimal place.
It is then right-justified by the Right tag {I had been looking for a justify tag, but it is TextAlign}
<Paragraph>
<TextRuns>
<TextRun>
<Value>=Format( Sum(Fields!ourField.Value, "DataSet2") , "N2") </Value>
<Style>
<FontFamily />
<Color>White</Color>
</Style>
</TextRun>
</TextRuns>
<Style>
<TextAlign>Right</TextAlign>
</Style>
</Paragraph>
Actually its possible to Justify text in SSRS report if you pass the value as HTML and use something to format the text into justify'ed html text before, in my case im using .NET C# to format the passed string to justified html text.
But before that we need to to configure our SSRS report to accept HTML for this we need to add a text box and create a placeholder.
to add a place holder click on the textbox until it lets you write text to it then right click and choose "Create placeholder..."
After you created the place holder you will be prompted to enter the properties of the placeholder, all you need to specify is Value and Markup type
be sure to select the Markup type as HTML and for the value specify the variable that will have the justified html text in our case lets call it transformedHtml.
Now we need to create a function that trasforms our string to justified HTML text
/// <summary>
///
/// </summary>
/// <param name="text">The text that we want to justify</param>
/// <param name="width">Justified text width in pixels</param>
/// <param name="useHtmlTagsForNewLines">if true returns the output as justified html if false returns the ouput as justified string</param>
/// <returns>Justified string</returns>
public string GetText(string text, int width, bool useHtmlTagsForNewLines = false)
{
var palabras = text.Split(' ');
var sb1 = new StringBuilder();
var sb2 = new StringBuilder();
var length = palabras.Length;
var resultado = new List<string>();
var graphics = Graphics.FromImage(new Bitmap(1, 1));
var font = new Font("Times New Roman", 11);
for (var i = 0; i < length; i++)
{
sb1.AppendFormat("{0} ", palabras[i]);
if (graphics.MeasureString(sb1.ToString(), font).Width > width)
{
resultado.Add(sb2.ToString());
sb1 = new StringBuilder();
sb2 = new StringBuilder();
i--;
}
else
{
sb2.AppendFormat("{0} ", palabras[i]);
}
}
resultado.Add(sb2.ToString());
var resultado2 = new List<string>();
string temp;
int index1, index2, salto;
string target;
var limite = resultado.Count;
foreach (var item in resultado)
{
target = " ";
temp = item.Trim();
index1 = 0; index2 = 0; salto = 2;
if (limite <= 1)
{
resultado2.Add(temp);
break;
}
while (graphics.MeasureString(temp, font).Width <= width)
{
if (temp.IndexOf(target, index2) < 0)
{
index1 = 0; index2 = 0;
target = target + " ";
salto++;
}
index1 = temp.IndexOf(target, index2);
temp = temp.Insert(temp.IndexOf(target, index2), " ");
index2 = index1 + salto;
}
limite--;
resultado2.Add(temp);
}
var res = string.Join(useHtmlTagsForNewLines ? "<br> " + Environment.NewLine : "\n", resultado2);
if (useHtmlTagsForNewLines)
res = $"<div>{res.Replace(" ", " ").Replace("<br> ", "<br>")}</div>";
return res;
}
By using this function we can transform any string to justified text and we can select if we want the output to be HTMl or simple string
then we can just call this method like
string text = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.";
string transformedHtml = GetText(text, 350, true);
and we get the output as folows:
In C#
In SSRS
Now this example mainly shows how to get justified text if your passing the values from C# code to ssrs reports but you could acchieve this if you would make the same function in a stored procedure that formats any text the same way. Hope this helps someone.