A better solution for Itext 7 Text fitting - optimization

I have a project that used Itext 5 and worked as intended.
Program had to put userInput in certain 'Chunks' inside paragraphs. Paragraphs have unmovable (chunks)words per line, and the userInput should scale in the space reserved for the userInput inside paragraph.
Old Project had the following code(made as example)
public class Oldway {
static final transient Font bold2 = FontFactory.getFont("Times-Roman", 10.0f, 1);
public static void main(String[] args) {
Document document = new Document();
document.setPageSize(PageSize.A4);
try {
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(new File("itext5.pdf")));
document.open();
Paragraph title = new Paragraph("Title of doc");
title.setAlignment(1);
document.add(title);
Paragraph dec= new Paragraph();
Chunk ch01 = new Chunk("Prev text ");
dec.add(ch01);
Chunk ch02 = new Chunk(getEmptySpace(42));
dec.add(ch02);
Chunk ch03 = new Chunk(" next Text");
dec.add(ch03);
document.add(dec);
float y = writer.getVerticalPosition(false);
float x2 = document.left() + ch01.getWidthPoint();
float x3 = x2 + ch02.getWidthPoint();
getPlainFillTest("Text to insert", document, y, x3, x2, writer, false);
document.close();
writer.flush();
} catch (FileNotFoundException | DocumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static Chunk getEmptySpace(int size) {
Chunk ch = new Chunk();
for(int i = 0;i<=size;i++) {
ch.append("\u00a0");
}
return new Chunk(ch);
}
public static void getPlainFillTest(String str,Document document,float y, float x1pos,
float x2pos, PdfWriter writer,boolean withTab) {
if(str.isEmpty() || str.isBlank()) {
str = "________";
}
Rectangle rec2 = null;
if(!withTab)
rec2 = new Rectangle(x2pos, y, x1pos-2,y+10);
else {
rec2 = new Rectangle(x2pos+35, y, x1pos+33,y+10);
}
BaseFont bf = bold2.getBaseFont();
PdfContentByte cb = writer.getDirectContent();
float fontSize = getMaxFontSize(bf, str,(int)rec2.getWidth(), (int)rec2.getHeight());
Phrase phrase = new Phrase(str, new Font(bf, fontSize));
ColumnText.showTextAligned(cb, Element.ALIGN_CENTER, phrase,
// center horizontally
(rec2.getLeft() + rec2.getRight()) / 2,
// shift baseline based on descent
rec2.getBottom() - bf.getDescentPoint(str, fontSize),0);
cb.saveState();//patrulaterul albastru
cb.setColorStroke(Color.BLUE);
cb.rectangle(rec2.getLeft(), rec2.getBottom(), rec2.getWidth(), rec2.getHeight());
cb.stroke();
cb.restoreState();
}
//stackoverflow solution
private static float getMaxFontSize(BaseFont bf, String text, int width, int height){
// avoid infinite loop when text is empty
if(text.isEmpty()){
return 0.0f;
}
float fontSize = 0.1f;
while(bf.getWidthPoint(text, fontSize) < width){
fontSize += 0.1f;
}
float maxHeight = measureHeight(bf, text, fontSize);
while(maxHeight > height){
fontSize -= 0.1f;
maxHeight = measureHeight(bf, text, fontSize);
};
return fontSize;
}
public static float measureHeight(BaseFont baseFont, String text, float fontSize)
{
float ascend = baseFont.getAscentPoint(text, fontSize);
float descend = baseFont.getDescentPoint(text, fontSize);
return ascend - descend;
}}
Now I'm trying to do the same thing in IText 7 and ...is not that easy!
I manage to create a working code, but its messy, and some things don't get the right coordinates. The Itext7 code(made as example):
public class Newway {
public static void main(String[] args) {
PdfWriter writer;
try {
writer = new PdfWriter(new File("test2.pdf"));
PdfDocument document = new PdfDocument(writer);
document.getDocumentInfo().addCreationDate();
document.getDocumentInfo().setTitle("Title");
document.setDefaultPageSize(PageSize.A4);
Document doc = new Document(document);
doc.setFontSize(12);
Paragraph par = new Paragraph();
Text ch01 = new Text("Prev Text ");
par.add(ch01);
Paragraph space = new Paragraph();
space.setMaxWidth(40);
for(int i=0;i<40;i++) {
par.add("\u00a0");
space.add("\u00a0");
}
Text ch02 = new Text(" next text");
par.add(ch02);
doc.add(par);
Paragraph linePara = new Paragraph().add("Test from UserInput")
.setTextAlignment(TextAlignment.CENTER).setBorder(new DottedBorder(1));
float width = doc.getPageEffectiveArea(PageSize.A4).getWidth();
float height = doc.getPageEffectiveArea(PageSize.A4).getHeight();
IRenderer primul = ch01.createRendererSubTree().setParent(doc.getRenderer());
IRenderer spaceR = space.createRendererSubTree().setParent(doc.getRenderer());
LayoutResult primulResult = primul.layout(new LayoutContext(new LayoutArea(1, new Rectangle(width,height))));
LayoutResult layoutResult = spaceR.layout(new LayoutContext(new LayoutArea(1, new Rectangle(width,height))));
Rectangle primulBox = ((TextRenderer) primul).getInnerAreaBBox();
Rectangle rect = ((ParagraphRenderer) spaceR).getInnerAreaBBox();
float rwidth = rect.getWidth();
float rheight = rect.getHeight();
float x = primulBox.getWidth()+ doc.getLeftMargin();
float y = rect.getY()+(rheight*2.05f);//rect.getY() is never accurate, is always below the paragraph. WHY ??
Rectangle towr = new Rectangle(x, y, rwidth, rheight*1.12f);//rheight on default is way too small
PdfCanvas pdfcanvas = new PdfCanvas(document.getFirstPage());
Canvas canvas = new Canvas(pdfcanvas, towr);
//from theinternet
float fontSizeL = 1;
float fontSizeR = 14;
while (Math.abs(fontSizeL - fontSizeR) > 1e-1) {
float curFontSize = (fontSizeL + fontSizeR) / 2;
linePara.setFontSize(curFontSize);
// It is important to set parent for the current element renderer to a root renderer
IRenderer renderer = linePara.createRendererSubTree().setParent(canvas.getRenderer());
LayoutContext context = new LayoutContext(new LayoutArea(1, towr));
if (renderer.layout(context).getStatus() == LayoutResult.FULL) {
// we can fit all the text with curFontSize
fontSizeL = curFontSize;
} else {
fontSizeR = curFontSize;
}
}
canvas.add(linePara);
new PdfCanvas(document.getFirstPage()).rectangle(towr).setStrokeColor(ColorConstants.BLACK).stroke();
canvas.close();
doc.close();
writer.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}}
The questions are
Is there a better, more elegant way to do this ?
Why rect.getY() is always below the paragraph? and how do I get Y to match the Paragraph real Y coorinate ?
Why the default 'rheight' is always too small? but (rheight*1.1f) works ?
(Optional) How do I set tab() space size in IText 7 ?

This way is quite good because it's taking into account all the possible model element settings and implications of the layout process. Your iText 5 alternative was good enough for basic case of Latin-based text without any modifications on the visual side. The iText 7 code you have is much more flexible and will still work if you use more complex layout settings, complex scripts etc. Also I see the iText 5 code is 105 lines in your example while the iText 7 code is 80 lines.
You are adding some magic +(rheight*2.05f); here while in reality what you are missing here is that when you draw via Canvas you don't have your margins defined anymore, so what you really need instead of rect.getY()+(rheight*2.05f); is rect.getY() + doc.getBottomMargin()
The issue comes from the fact that you are calculating rheight as renderer.getInnerAreaBBox() while this calculation does not take into account default margins that are applied to a paragraph. Margins are included into the occupied area but not the inner area bbox. To fix that, use renderer.getOccupiedArea().getBBox() instead. In this case there is not need to multiply rheight by a coefficient anymore.
The visual result is slightly different now but there is no magic constants anymore. Depending on what you are trying to really achieve you can tune the code further (add some margins here and there etc). But the code adapts well to the change in the user text.
Visual result before:
Visual result after:
Resultant code:
PdfWriter writer;
try {
writer = new PdfWriter(new File("test2.pdf"));
PdfDocument document = new PdfDocument(writer);
document.getDocumentInfo().addCreationDate();
document.getDocumentInfo().setTitle("Title");
document.setDefaultPageSize(PageSize.A4);
Document doc = new Document(document);
doc.setFontSize(12);
Paragraph par = new Paragraph();
Text ch01 = new Text("Prev Text ");
par.add(ch01);
Paragraph space = new Paragraph();
space.setMaxWidth(40);
for(int i=0;i<40;i++) {
par.add("\u00a0");
space.add("\u00a0");
}
Text ch02 = new Text(" next text");
par.add(ch02);
doc.add(par);
Paragraph linePara = new Paragraph().add("Test from UserInput")
.setTextAlignment(TextAlignment.CENTER).setBorder(new DottedBorder(1));
float width = doc.getPageEffectiveArea(PageSize.A4).getWidth();
float height = doc.getPageEffectiveArea(PageSize.A4).getHeight();
IRenderer primul = ch01.createRendererSubTree().setParent(doc.getRenderer());
IRenderer spaceR = space.createRendererSubTree().setParent(doc.getRenderer());
LayoutResult primulResult = primul.layout(new LayoutContext(new LayoutArea(1, new Rectangle(width,height))));
LayoutResult layoutResult = spaceR.layout(new LayoutContext(new LayoutArea(1, new Rectangle(width,height))));
Rectangle primulBox = ((TextRenderer) primul).getInnerAreaBBox();
Rectangle rect = ((ParagraphRenderer) spaceR).getOccupiedArea().getBBox();
float rwidth = rect.getWidth();
float rheight = rect.getHeight();
float x = primulBox.getWidth()+ doc.getLeftMargin();
float y = rect.getY() + doc.getBottomMargin();
Rectangle towr = new Rectangle(x, y, rwidth, rheight);
PdfCanvas pdfcanvas = new PdfCanvas(document.getFirstPage());
Canvas canvas = new Canvas(pdfcanvas, towr);
//from theinternet
float fontSizeL = 1;
float fontSizeR = 14;
while (Math.abs(fontSizeL - fontSizeR) > 1e-1) {
float curFontSize = (fontSizeL + fontSizeR) / 2;
linePara.setFontSize(curFontSize);
// It is important to set parent for the current element renderer to a root renderer
IRenderer renderer = linePara.createRendererSubTree().setParent(canvas.getRenderer());
LayoutContext context = new LayoutContext(new LayoutArea(1, towr));
if (renderer.layout(context).getStatus() == LayoutResult.FULL) {
// we can fit all the text with curFontSize
fontSizeL = curFontSize;
} else {
fontSizeR = curFontSize;
}
}
canvas.add(linePara);
new PdfCanvas(document.getFirstPage()).rectangle(towr).setStrokeColor(ColorConstants.BLACK).stroke();
canvas.close();
doc.close();
writer.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

Related

Annotation(stamp) missed after flattened

Annotation(PdfName.STAMP) missed after flattened by itextsharp5.5.13.1.
I have two pdfs. One does work, the other does not work.
Any ideas will be appreciated.
The code is following
string outFile = inputFile + "_f.pdf";
using (PdfReader pdfReader = new PdfReader(inputFileName))
{
PdfStamper pdfStamper = new PdfStamper(pdfReader, new FileStream(outFile, FileMode.Create, FileAccess.Write, FileShare.None))
//pdfStamper.FormFlattening = true;
//pdfStamper.FreeTextFlattening = true;
pdfStamper.AnnotationFlattening = true;
//pdfStamper.AcroFields.GenerateAppearances = true;
}
The cause is a bug in iTextSharp, annotation flattening does not correctly calculate the position for the flattened annotation if its bounding box does not use the origin as lower left corner.
If you look at the code of PdfStamperImp.FlattenAnnotations(bool), you'll quickly realize that the calculations in the if (app != null) block only make sense if the bounding box is located at the origin or no scaling is necessary for fitting the appearance bounding box into the annotation rectangle.
(As often the lower left corner of the bounding box is the origin, this does not pop up often.)
Thus, for flattening such annotations you have to use a different method for flattening, e.g. like this:
using (PdfReader pdfReader = new PdfReader(inputFileName))
{
PdfStamper pdfStamper = new PdfStamper(pdfReader, new FileStream(outFile, FileMode.Create, FileAccess.Write, FileShare.None));
ImprovedAnnotationFlattening(pdfStamper);
pdfStamper.Close();
}
with these helper methods:
void ImprovedAnnotationFlattening(PdfStamper pdfStamper)
{
double[] DEFAULT_MATRIX = { 1, 0, 0, 1, 0, 0 };
PdfReader reader = pdfStamper.Reader;
for (int page = 1; page <= reader.NumberOfPages; ++page)
{
PdfDictionary pageDic = reader.GetPageN(page);
PdfArray annots = pageDic.GetAsArray(PdfName.ANNOTS);
if (annots == null)
continue;
for (int idx = 0; idx < annots.Size; ++idx)
{
PdfObject annoto = annots.GetDirectObject(idx);
if (!(annoto is PdfDictionary))
continue;
PdfDictionary annDic = (PdfDictionary)annoto;
PdfNumber ff = annDic.GetAsNumber(PdfName.F);
int flags = ff != null ? ff.IntValue : 0;
if ((flags & PdfFormField.FLAGS_PRINT) == 0 || (flags & PdfFormField.FLAGS_HIDDEN) != 0)
continue;
PdfObject obj1 = annDic.Get(PdfName.AP);
if (obj1 == null)
continue;
PdfDictionary appDic = obj1 is PdfIndirectReference
? (PdfDictionary)PdfReader.GetPdfObject(obj1)
: (PdfDictionary)obj1;
PdfObject obj = appDic.Get(PdfName.N);
PdfStream objDict = appDic.GetAsStream(PdfName.N);
if (objDict != null)
{
Rectangle rect = PdfReader.GetNormalizedRectangle(annDic.GetAsArray(PdfName.RECT));
Rectangle bbox = PdfReader.GetNormalizedRectangle(objDict.GetAsArray(PdfName.BBOX));
PdfContentByte cb = pdfStamper.GetOverContent(page);
cb.SetLiteral("Q ");
PdfArray matrixArray = objDict.GetAsArray(PdfName.MATRIX);
double[] matrix = matrixArray != null ? matrixArray.AsDoubleArray() : DEFAULT_MATRIX;
AffineTransform transform = new AffineTransform(matrix);
double[] bboxCorners = { bbox.Left, bbox.Bottom, bbox.Right, bbox.Bottom, bbox.Right, bbox.Top, bbox.Left, bbox.Top };
transform.Transform(bboxCorners, 0, bboxCorners, 0, 4);
double minX = Min(bboxCorners, 0, 2);
double maxX = Max(bboxCorners, 0, 2);
double minY = Min(bboxCorners, 1, 2);
double maxY = Max(bboxCorners, 1, 2);
transform.preConcatenate(AffineTransform.GetTranslateInstance(-minX, -minY));
transform.preConcatenate(AffineTransform.GetScaleInstance(rect.Width/(maxX-minX), rect.Height/(maxY-minY)));
transform.preConcatenate(AffineTransform.GetTranslateInstance(rect.Left, rect.Bottom));
transform.GetMatrix(matrix);
cb.AddFormXObj(objDict, GenerateName(), matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]);
cb.SetLiteral("q ");
annots.Remove(idx);
--idx;
}
}
}
}
double Min(double[] array, int start, int step)
{
double result = array[start];
for (int i = start + step; i < array.Length; i+=step)
{
result = Math.Min(result, array[i]);
}
return result;
}
double Max(double[] array, int start, int step)
{
double result = array[start];
for (int i = start + step; i < array.Length; i += step)
{
result = Math.Max(result, array[i]);
}
return result;
}
PdfName GenerateName()
{
PdfName name = new PdfName("XXX" + formXObjectsCounter);
++formXObjectsCounter;
return name;
}
int formXObjectsCounter = 4711;
Beware: I just wrote these methods (copying as much as possible from the original flattening code) and only tested with your example files. Some border conditions might still have to be considered for general use. In particular I did not do all relevant null or 0 tests. Also I did not attempt to support proper tagging.

Traverse whole PDF and change blue color to black ( Change color of underlines as well) + iText

I am using below code to remove blue colors from pdf text. It is working fine. But it is not changing underlines color, but changing text color correctly.
original file part:
Manipulated File:
As you see in above manipulated file, underline color didn't change.
I am looking fix for this thing since two weeks, can anyone help on this. Below is my change color code:
public void testChangeBlackTextToGreenDocument(String source, String filename) throws IOException {
try (InputStream resource = getClass().getResourceAsStream(source);
PdfReader pdfReader = new PdfReader(source);
OutputStream result = new FileOutputStream(filename);
PdfWriter pdfWriter = new PdfWriter(result);
PdfDocument pdfDocument = new PdfDocument(pdfReader, pdfWriter);) {
PdfCanvasEditor editor = new PdfCanvasEditor() {
#Override
protected void write(PdfCanvasProcessor processor, PdfLiteral operator, List<PdfObject> operands) {
String operatorString = operator.toString();
if (TEXT_SHOWING_OPERATORS.contains(operatorString)) {
List<PdfObject> listobj = new ArrayList<>();
listobj.add(new PdfNumber(0));
listobj.add(new PdfNumber(0));
listobj.add(new PdfNumber(0));
listobj.add(new PdfLiteral("rg"));
if (currentlyReplacedBlack == null) {
Color currentFillColor =getGraphicsState().getFillColor();
if (ColorConstants.GREEN.equals(currentFillColor) || ColorConstants.CYAN.equals(currentFillColor) || ColorConstants.BLUE.equals(currentFillColor)) {
currentlyReplacedBlack = currentFillColor;
super.write(processor, new PdfLiteral("rg"), listobj);
}
}
} else if (currentlyReplacedBlack != null) {
if (currentlyReplacedBlack instanceof DeviceCmyk) {
List<PdfObject> listobj = new ArrayList<>();
listobj.add(new PdfNumber(0));
listobj.add(new PdfNumber(0));
listobj.add(new PdfNumber(0));
listobj.add(new PdfNumber(0));
listobj.add(new PdfLiteral("k"));
super.write(processor, new PdfLiteral("k"), listobj);
} else if (currentlyReplacedBlack instanceof DeviceGray) {
List<PdfObject> listobj = new ArrayList<>();
listobj.add(new PdfNumber(0));
listobj.add(new PdfLiteral("g"));
super.write(processor, new PdfLiteral("g"), listobj);
} else {
List<PdfObject> listobj = new ArrayList<>();
listobj.add(new PdfNumber(0));
listobj.add(new PdfNumber(0));
listobj.add(new PdfNumber(0));
listobj.add(new PdfLiteral("rg"));
super.write(processor, new PdfLiteral("rg"), listobj);
}
currentlyReplacedBlack = null;
}
super.write(processor, operator, operands);
}
Color currentlyReplacedBlack = null;
final List<String> TEXT_SHOWING_OPERATORS = Arrays.asList("Tj", "'", "\"", "TJ");
};
for (int i = 1; i <= pdfDocument.getNumberOfPages(); i++) {
editor.editPage(pdfDocument, i);
}
}
File file = new File(source);
file.delete();
}
Here is the original file.
https://raad-dev-test.s3.ap-south-1.amazonaws.com/36/2019-08-30/originalFile.pdf
Related Links:
Traverse whole PDF and change some attribute with some object in it using iText
Removing Watermark from PDF iTextSharp
Maven Dependcy Details:
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext7-core</artifactId>
<version>7.1.5</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.0.6</version>
</dependency>
Edited:
Accepted answer is not working for below files:
https://raad-dev-test.s3.ap-south-1.amazonaws.com/36/2019-08-30/021549Orig1s025_aprepitant_clinpharm_prea_Mac.pdf (Page 41)
https://raad-dev-test.s3.ap-south-1.amazonaws.com/36/2019-08-30/400_206494S5_avibactam_and_ceftazidine_unireview_prea_Mac.pdf (Page 60).
Please Help.
(The example code here uses iText 7 for Java. You mentioned neither the iText version nor your programming environment in tags or question text but your example code appears to indicate that this is your combination of choice.)
Replacing blue fill colors
The test you based your original code on attempts explicitly only to change text color. The "underline" in your document, though, is (as far as PDF drawing is concerned) not part of the text but instead drawn as a simple path. Thus, the underline explicitly is not touched by the original code and it has to be adapted for your task.
But actually your task, changing everything blue to black, is easier to implement than only changing the blue text, e.g.
try ( PdfReader pdfReader = new PdfReader(SOURCE_PDF);
PdfWriter pdfWriter = new PdfWriter(RESULT_PDF);
PdfDocument pdfDocument = new PdfDocument(pdfReader, pdfWriter) )
{
PdfCanvasEditor editor = new PdfCanvasEditor()
{
#Override
protected void write(PdfCanvasProcessor processor, PdfLiteral operator, List<PdfObject> operands)
{
String operatorString = operator.toString();
if (SET_FILL_RGB.equals(operatorString) && operands.size() == 4) {
if (isApproximatelyEqual(operands.get(0), 0) &&
isApproximatelyEqual(operands.get(1), 0) &&
isApproximatelyEqual(operands.get(2), 1)) {
super.write(processor, new PdfLiteral("g"), Arrays.asList(new PdfNumber(0), new PdfLiteral("g")));
return;
}
}
super.write(processor, operator, operands);
}
boolean isApproximatelyEqual(PdfObject number, float reference) {
return number instanceof PdfNumber && Math.abs(reference - ((PdfNumber)number).floatValue()) < 0.01f;
}
final String SET_FILL_RGB = "rg";
};
for (int i = 1; i <= pdfDocument.getNumberOfPages(); i++)
{
editor.editPage(pdfDocument, i);
}
}
(ChangeColor test testChangeFillRgbBlueToBlack)
Beware, this is merely a proof-of-concept, not a final and complete solution. In particular:
It merely looks at the fill (non-stroking) colors. In your case that suffices as both your text (as usual) and your underline use fill colors only - the underline actually is not drawn as a stroked line but instead as a slim, filled rectangle.
Only RGB blue (and only such blue set using the rg instruction, not set using sc or scn, let alone blues combined out of other colors using funky blend modes) is considered. This might be an issue particularly in case of documents explicitly designed for printing (likely using CMYK colors).
PdfCanvasEditor only inspects and edits the content stream of the page itself, not the content streams of displayed form XObjects or patterns; thus, some content may not be found. It can be generalized fairly easily.
The result:
Replacing blue fill and stroke colors
Testing the code above you soon found documents in which the underlines were not changed. As it turned out, these underlines are actually drawn as stroked lines, not as filled rectangle as above.
To also properly edit such documents, therefore, you must not only edit the fill colors but also the stroke colors, e.g. like this:
try ( PdfReader pdfReader = new PdfReader(SOURCE_PDF);
PdfWriter pdfWriter = new PdfWriter(RESULT_PDF);
PdfDocument pdfDocument = new PdfDocument(pdfReader, pdfWriter) )
{
PdfCanvasEditor editor = new PdfCanvasEditor()
{
#Override
protected void write(PdfCanvasProcessor processor, PdfLiteral operator, List<PdfObject> operands)
{
String operatorString = operator.toString();
if (SET_FILL_RGB.equals(operatorString) && operands.size() == 4) {
if (isApproximatelyEqual(operands.get(0), 0) &&
isApproximatelyEqual(operands.get(1), 0) &&
isApproximatelyEqual(operands.get(2), 1)) {
super.write(processor, new PdfLiteral("g"), Arrays.asList(new PdfNumber(0), new PdfLiteral("g")));
return;
}
}
if (SET_STROKE_RGB.equals(operatorString) && operands.size() == 4) {
if (isApproximatelyEqual(operands.get(0), 0) &&
isApproximatelyEqual(operands.get(1), 0) &&
isApproximatelyEqual(operands.get(2), 1)) {
super.write(processor, new PdfLiteral("G"), Arrays.asList(new PdfNumber(0), new PdfLiteral("G")));
return;
}
}
super.write(processor, operator, operands);
}
boolean isApproximatelyEqual(PdfObject number, float reference) {
return number instanceof PdfNumber && Math.abs(reference - ((PdfNumber)number).floatValue()) < 0.01f;
}
final String SET_FILL_RGB = "rg";
final String SET_STROKE_RGB = "RG";
};
for (int i = 1; i <= pdfDocument.getNumberOfPages(); i++)
{
editor.editPage(pdfDocument, i);
}
}
(ChangeColor tests testChangeRgbBlueToBlackControlOfNitrosamineImpuritiesInSartansRev and testChangeRgbBlueToBlackEdqmReportsIssuesOfNonComplianceWithToothMac)
The results:
and
Replacing different shades of blue from other RGB'ish color spaces
Testing the code above you again found documents in which the blue colors were not changed. As it turned out, these blue colors were not from the DeviceRGB standard RGB but instead from ICCBased colorspaces, profiled RGB color spaces to be more exact. In particular other color setting operators were used than before, sc / scn instead of rg. Furthermore, in one document not a pure blue 0 0 1 but instead a .17255 .3098 .63529 blue was used
If we assume that sc and scn instructions with three numeric arguments set some flavor of RGB colors as here (in general this is an oversimplification, Lab and other color spaces can also come with 4 components, but your documents seem RGB oriented) and are less strict in recognizing the blue color, we can generalize the code above as follows:
class AllRgbBlueToBlackConverter extends PdfCanvasEditor {
#Override
protected void write(PdfCanvasProcessor processor, PdfLiteral operator, List<PdfObject> operands)
{
String operatorString = operator.toString();
if (RGB_SETTER_CANDIDATES.contains(operatorString) && operands.size() == 4) {
if (isBlue(operands.get(0), operands.get(1), operands.get(2))) {
PdfNumber number0 = new PdfNumber(0);
operands.set(0, number0);
operands.set(1, number0);
operands.set(2, number0);
}
}
super.write(processor, operator, operands);
}
boolean isBlue(PdfObject red, PdfObject green, PdfObject blue) {
if (red instanceof PdfNumber && green instanceof PdfNumber && blue instanceof PdfNumber) {
float r = ((PdfNumber)red).floatValue();
float g = ((PdfNumber)green).floatValue();
float b = ((PdfNumber)blue).floatValue();
return b > .5f && r < .9f*b && g < .9f*b;
}
return false;
}
final Set<String> RGB_SETTER_CANDIDATES = new HashSet<>(Arrays.asList("rg", "RG", "sc", "SC", "scn", "SCN"));
}
(ChangeColor helper class)
Used like this
try ( PdfReader pdfReader = new PdfReader(INPUT);
PdfWriter pdfWriter = new PdfWriter(OUTPUT);
PdfDocument pdfDocument = new PdfDocument(pdfReader, pdfWriter) ) {
PdfCanvasEditor editor = new AllRgbBlueToBlackConverter();
for (int i = 1; i <= pdfDocument.getNumberOfPages(); i++)
{
editor.editPage(pdfDocument, i);
}
}
we get
and

Bad:Converting pdf to images

Convert class:
public void getImage(String pdfFilename) throws Exception{
List<byte[]> listImg = new ArrayList<>();
try (final PDDocument document = PDDocument.load(new File(pdfFilename))){
PDFRenderer pdfRenderer = new PDFRenderer(document);
for (int page = 0; page < document.getNumberOfPages(); ++page)
{
File file = new File("C:\\path1\\"+page+".png");
BufferedImage bim = pdfRenderer.renderImage(page);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(bim, "png",file);
System.out.println("!!!!");
// System.out.println(Arrays.toString(listImg.get(page)));
}
document.close();
} catch (IOException e){
System.err.println("Exception while trying to create pdf document - " + e);
}
}
Everything works well. All pdf files are converted, but if I use the class shw (this is very necessary for my project):
PdfDocument srcDoc = new PdfDocument(new PdfReader(DEST1));
Rectangle rect = srcDoc.getFirstPage().getPageSize();
System.out.println(rect);
Rectangle pageSize = new Rectangle(rect.getWidth(), rect.getHeight());
PdfDocument pdfDoc = new PdfDocument(new PdfWriter(dest));
pdfDoc.setDefaultPageSize(new PageSize(pageSize));
System.out.println(srcDoc.getNumberOfPages());
PdfCanvas content = new PdfCanvas(pdfDoc.addNewPage());
int n = 0;
for (int i =1 ; i <= srcDoc.getNumberOfPages(); i++) {
PdfFormXObject page = srcDoc.getPage(i).copyAsFormXObject(pdfDoc);
content.clip();
content.newPath();
content.addXObject(page,MainPdf.right_Margin-MainPdf.left_Margin,0);
content = new PdfCanvas(pdfDoc.addNewPage());
for (double y = 4.251969f; y <= 595; y += 14.1732) {
content.moveTo(0, y);
content.lineTo(420, y);
}
for (double x = 0; x <= 420; x += 14.1732) {
content.moveTo(x, 0);
content.lineTo(x, 595);
}
content.closePathStroke();
}
srcDoc.close();
pdfDoc.close();
}
Those images that have been converted to empty (contain nothing inside themselves, just a white background). Pdf not empty.
pdf:https://dropmefiles.com/UXedd
images:
The cause was the call
content.clip();
in the itext segment. This clips with an empty path. Adobe Reader ignores this, but PDFBox doesn't, so the current clipping path is empty so that nothing gets seen.
Per one of the comments, removing that call solves the problem. (I suspect that content.newPath(); isn't needed either)
I have also tried other viewers: PDF.js and GhostScript don't display it, Chrome and Edge display it.

Screenshot using SharpDX and EasyHook transparent

I have been struggling in taking screenshots with DirectX, the problem is, it's working but seems to be missing some colors (black outline is missing for example), and some stuff that is also rendered by DX doesn't show.
I have uploaded the images (how the image should render and the rendered one) and also the code, what might be the issue?
Correct Image
Rendered Image
Greetings
public class Screenshot
{
public static void TakeScreenshot(string name = null)
{
var t = new Thread((ThreadStart) delegate
{
// destination folder
var destinationFolder = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments, Environment.SpecialFolderOption.Create) + "\\My Software\\Screenshots";
if (!Directory.Exists(destinationFolder)) Directory.CreateDirectory(destinationFolder);
// file name
string filename = name;
if (string.IsNullOrEmpty(name))
filename = "image-" + (Directory.GetFiles(destinationFolder, "*.png").Count() + 1).ToString("000") + ".png";
// # of graphics card adapter
const int numAdapter = 0;
// # of output device (i.e. monitor)
const int numOutput = 0;
// Create DXGI Factory1
var factory = new Factory1();
var adapter = factory.GetAdapter1(numAdapter);
// Create device from Adapter
var device = new Device(adapter);
// Get DXGI.Output
var output = adapter.GetOutput(numOutput);
var output1 = output.QueryInterface<Output1>();
// Width/Height of desktop to capture
int width = ((SharpDX.Rectangle)output.Description.DesktopBounds).Width;
int height = ((SharpDX.Rectangle)output.Description.DesktopBounds).Height;
// Create Staging texture CPU-accessible
var textureDesc = new Texture2DDescription
{
CpuAccessFlags = CpuAccessFlags.Read,
BindFlags = BindFlags.None,
Format = Format.B8G8R8A8_UNorm,
Width = width,
Height = height,
OptionFlags = ResourceOptionFlags.None,
MipLevels = 1,
ArraySize = 1,
SampleDescription = { Count = 1, Quality = 0 },
Usage = ResourceUsage.Staging
};
var screenTexture = new Texture2D(device, textureDesc);
// Duplicate the output
var duplicatedOutput = output1.DuplicateOutput(device);
bool captureDone = false;
for (int i = 0; !captureDone; i++)
{
try
{
SharpDX.DXGI.Resource screenResource;
OutputDuplicateFrameInformation duplicateFrameInformation;
// Try to get duplicated frame within given time
duplicatedOutput.AcquireNextFrame(10000, out duplicateFrameInformation, out screenResource);
if (i > 0)
{
// copy resource into memory that can be accessed by the CPU
using (var screenTexture2D = screenResource.QueryInterface<Texture2D>())
device.ImmediateContext.CopyResource(screenTexture2D, screenTexture);
// Get the desktop capture texture
var mapSource = device.ImmediateContext.MapSubresource(screenTexture, 0, MapMode.Read, MapFlags.None);
// Create Drawing.Bitmap
var bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
var boundsRect = new System.Drawing.Rectangle(0, 0, width, height);
// Copy pixels from screen capture Texture to GDI bitmap
var mapDest = bitmap.LockBits(boundsRect, ImageLockMode.WriteOnly, bitmap.PixelFormat);
var sourcePtr = mapSource.DataPointer;
var destPtr = mapDest.Scan0;
for (int y = 0; y < height; y++)
{
// Copy a single line
Utilities.CopyMemory(destPtr, sourcePtr, width * 4);
// Advance pointers
sourcePtr = IntPtr.Add(sourcePtr, mapSource.RowPitch);
destPtr = IntPtr.Add(destPtr, mapDest.Stride);
}
// Release source and dest locks
bitmap.UnlockBits(mapDest);
device.ImmediateContext.UnmapSubresource(screenTexture, 0);
// Save the output
bitmap.Save(destinationFolder + Path.DirectorySeparatorChar + filename);
// Send Message
Main.Chat.AddMessage(null, "~b~Screenshot saved as " + filename);
// Capture done
captureDone = true;
}
screenResource.Dispose();
duplicatedOutput.ReleaseFrame();
}
catch (SharpDXException e)
{
if (e.ResultCode.Code != SharpDX.DXGI.ResultCode.WaitTimeout.Result.Code)
{
throw e;
}
}
}
});
t.IsBackground = true;
t.Start();
}
}

Using Angus J Clipper library to outline GraphicsPath text in vb.net

I am creating text in a GDI+ GraphicsPath, using DrawString, and then outputting this to PDF as a path.
This is all working perfectly at the moment.
The time I have an issue is when the chosen font causes the outlines to overlap each other. I have an image example though being a new user i can't upload it... (seems pointless..?..)
I found a library and also someone who has achieved the same as what I am looking for in this blog
I have converted the code snippet to vb.net though I keep getting an empty solution from the library.
Has anybody else managed to pass in a graphicsPath containing a string and retrieve outlined text using this or a similar library?
Here's some C# code that works ...
using ClipperLib;
static public void PathToPolygon(GraphicsPath path, Polygons polys, Single scale)
{
GraphicsPathIterator pathIterator = new GraphicsPathIterator(path);
pathIterator.Rewind();
polys.Clear();
PointF[] points = new PointF[pathIterator.Count];
byte[] types = new byte[pathIterator.Count];
pathIterator.Enumerate(ref points, ref types);
int i = 0;
while (i < pathIterator.Count)
{
Polygon pg = new Polygon();
polys.Add(pg);
do {
IntPoint pt = new IntPoint((int)(points[i].X * scale), (int)(points[i].Y * scale));
pg.Add(pt);
i++;
}
while (i < pathIterator.Count && types[i] != 0);
}
}
static private PointF[] PolygonToPointFArray(Polygon pg, float scale)
{
PointF[] result = new PointF[pg.Count];
for (int i = 0; i < pg.Count; ++i)
{
result[i].X = (float)pg[i].X / scale;
result[i].Y = (float)pg[i].Y / scale;
}
return result;
}
private void DrawBitmap()
{
Font f = new Font("Arial", 90);
Pen myPen = new Pen(Color.FromArgb(196, 0xC3, 0xC9, 0xCF), (float)0.6);
SolidBrush myBrush = new SolidBrush(Color.FromArgb(127, 0xDD, 0xDD, 0xF0));
path.Reset();
Polygons polys;
path.AddString("ABC", f.FontFamily, (int)f.Style, f.Size, new Point(100, 100), null);
path.Flatten();
//scale all points up by 100 because Clipper uses integer coordinates
PathToPolygon(path, polys, 100);
path.Reset();
//offset polys remembering to multiply delta by scaling amount ...
polys = Clipper.OffsetPolygons(polys, 7 * 100, JoinType.jtRound);
for (int i = 0; i < polys.Count(); i++)
{
//reverses scaling ...
PointF[] pts2 = PolygonToPointFArray(polys[i], 100);
path.AddPolygon(pts2);
}
newgraphic.FillPath(myBrush, path);
newgraphic.DrawPath(myPen, path);
}