How to specify the position of parsed XHTML using itextsharp - xhtml

I use itextsharp for creating a pdf. I need to place XHTML on it so I uase the XMLWorkerHelper class:
iTextSharp.tool.xml.XMLWorkerHelper worker = iTextSharp.tool.xml.XMLWorkerHelper.GetInstance();
worker.ParseXHtml(pdfWrite, doc, new StringReader(sb.ToString()));
However I would like to specify a position for the parsed XHTML. How do I do that?
EDIT:
I thought I will post the code in the case someone else runs into this. The link provided below was for JAVA and in C# things work a bit different.
First you need a class for gathering the Elements:
class ElementHandlerClass : iTextSharp.tool.xml.IElementHandler
{
public List<IElement> elements = new List<IElement>();
public void Add(iTextSharp.tool.xml.IWritable input)
{
if (input is iTextSharp.tool.xml.pipeline.WritableElement)
{
elements.AddRange(((iTextSharp.tool.xml.pipeline.WritableElement)input).Elements());
}
}
}
Then you use it
ElementHandlerClass ehc = new ElementHandlerClass();
worker.ParseXHtml(ehc, new StringReader(sb.ToString()));
Now you have the elements. Next step is to create a ColumnText and fill it with the Elements:
iTextSharp.text.pdf.ColumnText ct = new iTextSharp.text.pdf.ColumnText(pdfWrite.DirectContent);
ct.SetSimpleColumn(200, 300, 300, 500);
foreach (IElement element in ehc.elements)
ct.AddElement(element);
ct.Go();

You need to combine the answers of two previous questions on StackOverflow.
The first answer you need, is the one to How to get particular html table contents to write it in pdf using itext
In this answer, you learn how to parse an XHTML source into a list of Element objects.
Once you have this list, you need the answer to itext ColumnText ignores alignment
You can create a ColumnText object, define a rectangle with the setSimpleColumn() method, add all the elements retrieved from the XHTML using XML Worker with the addElement() method, and go() to add that content.

Related

Thymeleaf custom dialect with fragments

In my Spring Boot 2 project I have Thymeleaf fragments to generate form elements. For example:
<input th:replace="component/admin_form :: text (formElement = ${vm.getElementStatus()}, class='css-class-a'))" />
The code above generates a complex div block with label, input field and error block.
I would like to simplify this syntax. My idea was to create a custom dialect with a custom tag and write this:
<admin-form:text value="${vm.getElementLastName()}" class="css-class-a"/>
The second one is easier to read, it clearly indicates for the designers that this is a special element. Besides this, it would be easier to change themes as I only need to change the concrete fragment location in the tag processor and not hundreds of th:replace value.
It is also important that I don't want to build the complex html layout in the tag processor just want to somehow import a fragment. So designers can modify the html fragment without java code changes.
I was able to create the custom dialect and create the custom tag that generates a html block:
#Override
protected void doProcess(ITemplateContext context, IProcessableElementTag tag, IElementTagStructureHandler structureHandler) {
final IModelFactory modelFactory = context.getModelFactory();
final IModel model = modelFactory.createModel();
model.add(modelFactory.createOpenElementTag("div", "class", "test"));
model.add(modelFactory.createText("This is my custom element"));
model.add(modelFactory.createCloseElementTag("div"));
structureHandler.replaceWith(model, false);
}
But I don't know how to import a fragment in my custom tag.
Is it possible anyhow?
The doProcess of your <admin-form:text> tag could create a dummy tag with a th:replace attribute that includes the "complex input" fragment:
Map<String, String> attributes = new HashMap<>();
attributes.put("th:replace", "/pathToMyFragment/complexInput::myfragname");
IOpenElementTag newTag = modelFactory.createOpenElementTag("div", attributes, AttributeValueQuotes.DOUBLE, false);
model.replace(0, newTag);
or something similar (take care of closing the tag and so on).
The result would be to replace
<admin-form:text/>
with
<div th:replace="/pathToMyFragment/complexInput::myfragname"></div>
which in turn would be processed into the final HTML.
If you need to keep the original tag attributes (like class="css-class-a") you can get them from the original tag with model.get(0).getAttributeMap() and add them as a local variable with structureHandler.setLocalVariable("originalAttributes", theAttributeMap); to use in the final fragment, for example.

Using Google Sites and Google App Scripts, how can I dynamically change the CSS class of an HTML element?

I am using Google Sites for my webpage and I want parts of it to be kind of dynamic. Hence, what I want to achieve is to change the CSS class associated with certain HTML element in the presence of an on click event.
E.g.:
Initial situation:
<div class="foo">Hello</div>
World
After clicking on "World":
<div class="bar">Hello</div>
World
Add an ID to the div, then:
function method() {
var myDiv = document.getElementById("myDIV");
myDiv.className = "bar";
}
Alternatively you can get all elements with that particular class name, then loop through them to change their class name.
function method() {
var myDivs = document.getElementsByClassName("foo");
for (var i = 0; i < myDivs.length; i++)
myDivs[i].className = "bar";
}
Now of course you can add if/else statements to check for certain conditions before making changes.
Now that I understand what you are looking for, Check this link.
There is a get and set method for the HTML content of the page requested. By manipulating the generated string, it should be possible to then change the div to the required class and other such things. This should be a good start, we'll have to play around with the rest.
This might also be helpful, but I believe it has several limitations.

Is there a way to filter out hidden elements with css?

as an example some html has several elements which have the css path table.class1.class2[role="menu"] but only one of these elements will be visible at any given time, so I want to get only the one that is visible.
can I adjust my css path to narrow it down?
Possibly use Linq to get the list. I am not sure which language you are using. But, similar concept can be applied using any of them. Using Linq
to accomplish this kind of scenario is very simple in C#
public IWebElement Test()
{
//selector
By bycss = By.CssSelector("table.class1.class2[role='menu']");
return Driver.FindElements(bycss).ToList().FirstOrDefault(d => d.Displayed);
}
And, make sure to import
using System.Linq; if you are using C#
In Java you can do something like this[not using lambdas]
List<WebElement> visibleList = null;
//selector
By byCss = By.cssSelector("table.class1.class2[role='menu']");
//list of visible and hidden elements
Iterator<WebElement> iterator = driver.findElements(byCss).iterator();
while (iterator.hasNext()){
WebElement element = iterator.next();
if (element.isDisplayed()){
//building the list of visible elements
visibleList.add(element);
}
}
//get the first item of the list
//you can return all if needed
return visibleList.get(0);
In Java, you can use WebElement.isDisplayed().

Removing all elements from HTML that have given class using Agility Pack

I'm trying to select all elements that have a given class and remove them from a HTML string.
This is what I have so far it doesn't seem to remove anything although the source shows clearly 4 elements with that class name.
// Filter page HTML to display required content
HtmlAgilityPack.HtmlDocument htmlDoc = new HtmlAgilityPack.HtmlDocument();
// filePath is a path to a file containing the html
htmlDoc.LoadHtml(pageHTML);
// ParseErrors is an ArrayList containing any errors from the Load statement);
if (!htmlDoc.ParseErrors.Any())
{
// Remove all elements marked with pdf-ignore class
HtmlNodeCollection nodes = htmlDoc.DocumentNode.SelectNodes("//body[#class='pdf-ignore']");
// Remove the collection from above
foreach (var node in nodes)
{
node.Remove();
}
}
EDIT: Just to clarify the document is parsing and the SelectNodes line is being hit, just not returning anything.
Here is a snippet of the html:
<input type=\"submit\" name=\"ctl00$MainContent$PrintBtn\" value=\"Print Shotlist\" onclick=\"window.print();\" id=\"MainContent_PrintBtn\" class=\"pdf-ignore\">
EDIT: in your updated answer you posted a part of the HTML string an <input> element declaration, but you're trying to match a <body> element with the class pdf-ignore (according to your expression //body[#class='pdf-ignore']).
If you want to match all the elements from the document with this class you should use:
var nodes = htmlDoc.DocumentNode.SelectNodes("//*[contains(#class,'pdf-ignore')]");
code to get your nodes. This will match all the elements with the class name specified.
Your code is seems to be correct except the one detail: the condition htmlDoc.ParseErrors == null. You select and remove nodes ONLY if the ParseErrors property (which is a type of IEnumerable<HtmlParseError>) is null, but actually if no errors found this property returns an empty list. So changing your code to:
if (!htmlDoc.ParseErrors.Any())
{
// some logic here
}
should solve the issue.
Your xpath is probably not matching: have you tried "//div[class='pdf-ignore']" (no "#")?

How to create an empty XHTML compliant P node using HTML Agility Pack 1.4.0?

However I try to create an HTMLNode for the P tag and inject it into the HTMLDocument DOM, it always appears as an unclosed tag. For example.
// different ways I've tried creating the node:
var p = HtmlNode.CreateNode("<p />");
var p = HtmlNode.CreateNode("<p></p>");
var p = HtmlNode.CreateNode("<p>");
var p = HtmlTextNode.CreateNode("<p></p>");
// some other properties I've played with:
p.Name = "p";
p.InnerHtml = "";
They all end up as just <p> in the output after using the .Save() method.
I want it properly closed for XHTML like <p /> or <p></p>. Either is fine.
My workaround: What I can do is issue CreateNode("<p> </p>") (with a space in between) and it retains the entire source, but I think there has to be a better way.
Other options tried or considered:
When I turn on the option .OutputAsXml it escapes the existing entities, for example turns to &nbsp; which is not ideal, and it doesn't close my injected P tag.
when I enable the option .OptionWriteEmptyNodes it still doesn't close my injected P tag.
I see the Agility Pack contains the enum HtmlElementFlag with values Closed, Empty, CData, CanOverlap (Closed might be useful) but cannot see where I would apply it when creating a new element/node.
I found the answer: the P tag has to be created off the HtmlDocument instance using the CreateElement(..) factory method like so:
var hdoc = new HtmlDocument(); // HTML doc instance
// ... stuff
HtmlNode p = hdoc.CreateElement("p"); // << will close itself for XHTML.
Then P will close itself like <p />.
If you instead create an HtmlNode instance using the HtmlNode.CreateNode(..) factory method like I was trying in the question, it behaves differently in the DOM as far as closure.
HtmlNode.ElementsFlags["p"] = HtmlElementFlag.Closed;
Default value for "p" is "Empty | Closed". You should to set it as "Closed" to return:
<p></p>

Resources