问题
I am converting HTML to a PDF using iText 7. I need the PDF to be accessible (508 compliant with appropriate tags, etc), but, no matter what markup I put on a table, accessibility checkers give the same error: "Table header cell has no associated subcells". I've tried setting scope, headers, etc... nothing seems to work. Here is an example of one of the tables but all of them have the same issue:
<table class="problems" summary="Patient's diagnosed problems and associated ICD codes.">
<thead>
<tr>
<th scope="col" id="problem-header">
Problem
</th>
<th scope="col" id="icd-code-header">
Code
</th>
</tr>
</thead>
<tbody>
<tr>
<td headers="problem-header">Some Problem</td>
<td headers="icd-code-header">Some ICD Code</td>
</tr>
</tbody>
</table>
Any help would be appreciated. Thank you so much.
EDIT: I forgot to mention, I am using the .NET version of iText 7.
EDIT 2: Here is the code that converts the HTML to PDF:
public class AccessiblePdfService : IAccessiblePdfService
{
private static readonly string[] FontPaths = ConfigurationManager.AppSettings["FontPaths"].Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
public void createPdf(string html, string dest, PdfTypes type = PdfTypes.RefDoc) //string resources
{
FileStream outputStream = new FileStream(dest, FileMode.Create, FileAccess.Write);
WriterProperties writerProperties = new WriterProperties();
//Add metadata
writerProperties.AddXmpMetadata();
PdfWriter pdfWriter = new PdfWriter(outputStream, writerProperties);
PdfDocument pdfDoc = new PdfDocument(pdfWriter);
pdfDoc.GetCatalog().SetLang(new PdfString("en-US"));
//Set the document to be tagged
pdfDoc.SetTagged();
pdfDoc.GetCatalog().SetViewerPreferences(new PdfViewerPreferences().SetDisplayDocTitle(true));
//Set meta tags
PdfDocumentInfo pdfMetaData = pdfDoc.GetDocumentInfo();
pdfMetaData.SetAuthor("SOME STRING");
pdfMetaData.AddCreationDate();
pdfMetaData.GetProducer();
pdfMetaData.SetCreator("SOME STRING");
switch (type)
{
case PdfTypes.RefDoc:
pdfMetaData.SetKeywords("SOME STRING");
pdfMetaData.SetSubject("SOME STRING");
break;
case PdfTypes.PatientRoi:
pdfMetaData.SetKeywords("SOME STRING");
pdfMetaData.SetSubject("SOME STRING");
break;
case PdfTypes.RoiAdmin:
pdfMetaData.SetKeywords("SOME STRING");
pdfMetaData.SetSubject("SOME STRING");
break;
default:
break;
}
//Title is derived from html
// pdf conversion
ConverterProperties props = new ConverterProperties();
FontProvider fp = new FontProvider();
fp.AddStandardPdfFonts();
foreach (var path in FontPaths)
{
fp.AddFont(path);
}
props.SetFontProvider(fp);
DefaultTagWorkerFactory tagWorkerFactory = new AccessibilityTagWorkerFactory();
props.SetTagWorkerFactory(tagWorkerFactory);
HtmlConverter.ConvertToPdf(html, pdfDoc, props);
pdfDoc.Close();
}
}
EDIT 3: Here is the AccessibilityTagWorkerFactory (keep in mind, the tables that I want to act like tables are not marked with the class, "make-table-div" and there shouldn't be affected by the customizations in this class:
public class AccessibilityTagWorkerFactory : DefaultTagWorkerFactory
{
public override ITagWorker GetCustomTagWorker(IElementNode tag, ProcessorContext context)
{
bool hasClass = false;
foreach (var attribute in tag.GetAttributes())
{
if (attribute.GetKey() == "class")
{
hasClass = true;
}
}
if (hasClass && tag.GetAttribute(AttributeConstants.CLASS).Contains("make-h1"))
{
return new HRoleSpanTagWorker(tag, context, StandardRoles.H1);
}
if (hasClass && tag.GetAttribute(AttributeConstants.CLASS).Contains("make-h2"))
{
return new HRoleSpanTagWorker(tag, context, StandardRoles.H2);
}
if (hasClass && tag.GetAttribute(AttributeConstants.CLASS).Contains("make-table-div"))
{
return new DivRoleTableTagWorker(tag, context);
}
return base.GetCustomTagWorker(tag, context);
}
}
回答1:
After working with Jon Reilly on the iText team, this was the final solution that worked for me (column IDs and associated headers aren't needed... just Scope)
public class ThWithScopeTagWorker : ThTagWorker
{
public ThWithScopeTagWorker(IElementNode element, ProcessorContext context) : base(element, context)
{
}
public override void ProcessEnd(IElementNode element, ProcessorContext context)
{
base.ProcessEnd(element, context);
IPropertyContainer elementResult = base.GetElementResult();
if (elementResult is IAccessibleElement)
{
((IAccessibleElement)elementResult).GetAccessibilityProperties().SetRole(StandardRoles.TH);
//Can use this in the future in case we have th elements with different scope than "col"
string htmlScope = element.GetAttribute("scope"); //This is the scope="XXX" in your HTML
AccessibilityProperties properties = ((IAccessibleElement)elementResult).GetAccessibilityProperties();
//Could add "Row" if needed based on htmlScope string above.
//For my purposes, all th elements were scope="col"
properties.AddAttributes(new PdfStructureAttributes("Table").AddEnumAttribute("Scope", "Column"));
}
}
}
and this:
public class AccessibilityTagWorkerFactory : DefaultTagWorkerFactory
{
public override ITagWorker GetCustomTagWorker(IElementNode tag, ProcessorContext context)
{
//...
if (tag.Name() == "th")
{
return new ThWithScopeTagWorker(tag, context);
}
return base.GetCustomTagWorker(tag, context);
}
}
来源:https://stackoverflow.com/questions/48771422/itext-7-pdf-accessibility-table-header-cell-has-no-associated-subcells