WebHU - Programozási kérdések és válaszok

Hogyan szerezheti meg az oldal tartalmának magasságát a pdfbox segítségével

Meg lehet kérdezni az oldal tartalmának magasságát a pdfbox segítségével? Azt hiszem, mindent megpróbáltam, de mindegyik (PDRectangle) az oldal teljes magasságát adja vissza: 842. Először arra gondoltam, hogy ez azért van, mert az oldalszám az oldal alján van, de amikor megnyitottam a pdf-et Illustratorban, a teljes tartalom benne van. összetett elem, és nem terjed ki az oldal teljes magasságára. Tehát ha az illusztrátor külön elemként látja és ki tudja számítani a magasságát, akkor azt hiszem, ezt a pdfboxban is meg kell tenni.

Példa oldal:

adja meg itt a kép leírását

04.02.2015

  • Ha a dokumentumot illusztrátorral hozták volna létre... Az Illustrator meghagyja a saját, védett információit a dokumentumban, amelyből valamilyen összetett elemet mutathat. Ha megosztja a kérdéses PDF-et, meg tudjuk állapítani, hogy létezik-e megfelelő PDF-struktúra, vagy ez puszta illusztrálás. 04.02.2015
  • A PDF-et az alkalmazás hozza létre, semmi köze az Illustratorhoz, amelyet csak a pdf ellenőrzésére használtak. 04.02.2015
  • Ez lehet egy xobject vagy egy klip elérési út, vagy bármi hasonló. Ha meg tudod osztani a PDF-et... 04.02.2015
  • Itt található a PDF: d.pr/f/137PF a mezőkben lévő szöveg több sorból állhat, így a a méret nem állandó. Meg lehet kapni ennek a fejlécnek a pozícióját és méretét (fehér + szürke doboz)? 04.02.2015
  • Első pillantásra csak egy klip útvonalat látok ott. Ezek megtalálásához tartalomfolyam-elemzés szükséges. Holnap még utánanézek az irodában. 04.02.2015
  • A fő PDF módosult (a phantomjs által most generált): d.pr/f/15uBF, de a probléma marad: Nem tudom azonosítani az utolsó alakzatot és annak pozícióját a fejlécben. 05.02.2015

Válaszok:


1

Általában

A PDF specifikációja lehetővé teszi, hogy a PDF számos oldalhatárt adjon meg, lásd ezt a választ. Ezeken kívül a tartalmi határok csak az oldal tartalmából származtathatók, pl. tól től

  • #P2#
    #P3#
  • #P4#
    #P5#
  • ...

Bármelyik megtalálásához elemezni kell az oldal tartalmát, meg kell keresni a megfelelő műveleteket, és ki kell számítani az eredményül kapott határokat.

Az OP esetében

Minden PDF-minta csak egy oldalhatárt határoz meg, a MediaBox-ot. Így az összes többi PDF-oldalhatár (CropBox, BleedBox, TrimBox, ArtBox) ez az alapértelmezett. Így nem csoda, hogy a próbálkozásaidban

mindegyik (PDRectangle) az oldal teljes magasságát adja vissza: 842

Egyik sem tartalmaz XObject-et, de mindkettő vágógörbét használ.

  • Teszt-pdf4.pdf esetén:

    Start at: 28.31999969482422, 813.6799926757812
    Line to: 565.9199829101562, 813.6799926757812
    Line to: 565.9199829101562, 660.2196655273438
    Line to: 28.31999969482422, 660.2196655273438
    Line to: 28.31999969482422, 813.6799926757812
    

    (Ez egyezhet a kérdésében szereplő vázlattal.)

  • Teszt-pdf5.pdf esetén:

    Start at: 23.0, 34.0
    Line to: 572.0, 34.0
    Line to: 572.0, -751.0
    Line to: 23.0, -751.0
    Line to: 23.0, 34.0
    

    és

    Start at: 23.0, 819.0
    Line to: 572.0, 819.0
    Line to: 572.0, 34.0
    Line to: 23.0, 34.0
    Line to: 23.0, 819.0
    

A vázlattal való egyezés miatt azt feltételezném, hogy az Illustrator mindent megrajzoltnak tekint, miközben egy nem triviális vágógörbe van érvényben, egy összetett elem a vágógörbe szegélyként.

Vágógörbe keresése a PDFBox segítségével

A PDFBox segítségével megtaláltam a fent említett vágógörbéket. A fejlesztés alatt álló 2.0.0-s verzió jelenlegi SNAPSHOT-ját használtam, mivel a szükséges API-k sokat javultak a jelenlegi 1.8.8-as verzióhoz képest.

Kibővítettem PDFGraphicsStreamEngine egy ClipPathFinder osztályra:

public class ClipPathFinder extends PDFGraphicsStreamEngine implements Iterable<Path>
{
    public ClipPathFinder(PDPage page)
    {
        super(page);
    }

    //
    // PDFGraphicsStreamEngine overrides
    //
    public void findClipPaths() throws IOException
    {
        processPage(getPage());
    }

    @Override
    public void appendRectangle(Point2D p0, Point2D p1, Point2D p2, Point2D p3) throws IOException
    {
        startPathIfNecessary();
        currentPath.appendRectangle(toFloat(p0), toFloat(p1), toFloat(p2), toFloat(p3));
    }

    @Override
    public void drawImage(PDImage pdImage) throws IOException { }

    @Override
    public void clip(int windingRule) throws IOException
    {
        currentPath.complete(windingRule);
        paths.add(currentPath);
        currentPath = null;
    }

    @Override
    public void moveTo(float x, float y) throws IOException
    {
        startPathIfNecessary();
        currentPath.moveTo(x, y);
    }

    @Override
    public void lineTo(float x, float y) throws IOException
    {
        currentPath.lineTo(x, y);
    }

    @Override
    public void curveTo(float x1, float y1, float x2, float y2, float x3, float y3) throws IOException
    {
        currentPath.curveTo(x1, y1, x2, y2, x3, y3);
    }

    @Override
    public Point2D.Float getCurrentPoint() throws IOException
    {
        return currentPath.getCurrentPoint();
    }

    @Override
    public void closePath() throws IOException
    {
        currentPath.closePath();
    }

    @Override
    public void endPath() throws IOException
    {
        currentPath = null;
    }

    @Override
    public void strokePath() throws IOException
    {
        currentPath = null;
    }

    @Override
    public void fillPath(int windingRule) throws IOException
    {
        currentPath = null;
    }

    @Override
    public void fillAndStrokePath(int windingRule) throws IOException
    {
        currentPath = null;
    }

    @Override
    public void shadingFill(COSName shadingName) throws IOException
    {
        currentPath = null;
    }

    void startPathIfNecessary()
    {
        if (currentPath == null)
            currentPath = new Path();
    }

    Point2D.Float toFloat(Point2D p)
    {
        if (p == null || (p instanceof Point2D.Float))
        {
            return (Point2D.Float)p;
        }
        return new Point2D.Float((float)p.getX(), (float)p.getY());
    }

    //
    // Iterable<Path> implementation
    //
    public Iterator<Path> iterator()
    {
        return paths.iterator();
    }

    Path currentPath = null;
    final List<Path> paths = new ArrayList<Path>();
}

Ezt a helper osztályt használja az elérési utak ábrázolására:

public class Path implements Iterable<Path.SubPath>
{
    public static class Segment
    {
        Segment(Point2D.Float start, Point2D.Float end)
        {
            this.start = start;
            this.end = end;
        }

        public Point2D.Float getStart()
        {
            return start;
        }

        public Point2D.Float getEnd()
        {
            return end;
        }

        final Point2D.Float start, end; 
    }

    public class SubPath implements Iterable<Segment>
    {
        public class Line extends Segment
        {
            Line(Point2D.Float start, Point2D.Float end)
            {
                super(start, end);
            }

            //
            // Object override
            //
            @Override
            public String toString()
            {
                StringBuilder builder = new StringBuilder();
                builder.append("    Line to: ")
                       .append(end.getX())
                       .append(", ")
                       .append(end.getY())
                       .append('\n');
                return builder.toString();
            }
        }

        public class Curve extends Segment
        {
            Curve(Point2D.Float start, Point2D.Float control1, Point2D.Float control2, Point2D.Float end)
            {
                super(start, end);
                this.control1 = control1;
                this.control2 = control2;
            }

            public Point2D getControl1()
            {
                return control1;
            }

            public Point2D getControl2()
            {
                return control2;
            }

            //
            // Object override
            //
            @Override
            public String toString()
            {
                StringBuilder builder = new StringBuilder();
                builder.append("    Curve to: ")
                       .append(end.getX())
                       .append(", ")
                       .append(end.getY())
                       .append(" with Control1: ")
                       .append(control1.getX())
                       .append(", ")
                       .append(control1.getY())
                       .append(" and Control2: ")
                       .append(control2.getX())
                       .append(", ")
                       .append(control2.getY())
                       .append('\n');
                return builder.toString();
            }

            final Point2D control1, control2; 
        }

        SubPath(Point2D.Float start)
        {
            this.start = start;
            currentPoint = start;
        }

        public Point2D getStart()
        {
            return start;
        }

        void lineTo(float x, float y)
        {
            Point2D.Float end = new Point2D.Float(x, y);
            segments.add(new Line(currentPoint, end));
            currentPoint = end;
        }

        void curveTo(float x1, float y1, float x2, float y2, float x3, float y3)
        {
            Point2D.Float control1 = new Point2D.Float(x1, y1);
            Point2D.Float control2 = new Point2D.Float(x2, y2);
            Point2D.Float end = new Point2D.Float(x3, y3);
            segments.add(new Curve(currentPoint, control1, control2, end));
            currentPoint = end;
        }

        void closePath()
        {
            closed = true;
            currentPoint = start;
        }

        //
        // Iterable<Segment> implementation
        //
        public Iterator<Segment> iterator()
        {
            return segments.iterator();
        }

        //
        // Object override
        //
        @Override
        public String toString()
        {
            StringBuilder builder = new StringBuilder();
            builder.append("  {\n    Start at: ")
                   .append(start.getX())
                   .append(", ")
                   .append(start.getY())
                   .append('\n');
            for (Segment segment : segments)
                builder.append(segment);
            if (closed)
                builder.append("    Closed\n");
            builder.append("  }\n");
            return builder.toString();
        }

        boolean closed = false;
        final Point2D.Float start;
        final List<Segment> segments = new ArrayList<Path.Segment>();
    }

    public class Rectangle extends SubPath
    {
        Rectangle(Point2D.Float p0, Point2D.Float p1, Point2D.Float p2, Point2D.Float p3)
        {
            super(p0);
            lineTo((float)p1.getX(), (float)p1.getY());
            lineTo((float)p2.getX(), (float)p2.getY());
            lineTo((float)p3.getX(), (float)p3.getY());
            closePath();
        }

        //
        // Object override
        //
        @Override
        public String toString()
        {
            StringBuilder builder = new StringBuilder();
            builder.append("  {\n    Rectangle\n    Start at: ")
                   .append(start.getX())
                   .append(", ")
                   .append(start.getY())
                   .append('\n');
            for (Segment segment : segments)
                builder.append(segment);
            if (closed)
                builder.append("    Closed\n");
            builder.append("  }\n");
            return builder.toString();
        }
    }

    public int getWindingRule()
    {
        return windingRule;
    }

    void complete(int windingRule)
    {
        finishSubPath();
        this.windingRule = windingRule;
    }

    void appendRectangle(Point2D.Float p0, Point2D.Float p1, Point2D.Float p2, Point2D.Float p3) throws IOException
    {
        finishSubPath();
        currentSubPath = new Rectangle(p0, p1, p2, p3);
        finishSubPath();
    }

    void moveTo(float x, float y) throws IOException
    {
        finishSubPath();
        currentSubPath = new SubPath(new Point2D.Float(x, y));
    }

    void lineTo(float x, float y) throws IOException
    {
        currentSubPath.lineTo(x, y);
    }

    void curveTo(float x1, float y1, float x2, float y2, float x3, float y3) throws IOException
    {
        currentSubPath.curveTo(x1, y1, x2, y2, x3, y3);
    }

    Point2D.Float getCurrentPoint() throws IOException
    {
        return currentPoint;
    }

    void closePath() throws IOException
    {
        currentSubPath.closePath();
        finishSubPath();
    }

    void finishSubPath()
    {
        if (currentSubPath != null)
        {
            subPaths.add(currentSubPath);
            currentSubPath = null;
        }
    }

    //
    // Iterable<Path.SubPath> implementation
    //
    public Iterator<SubPath> iterator()
    {
        return subPaths.iterator();
    }

    //
    // Object override
    //
    @Override
    public String toString()
    {
        StringBuilder builder = new StringBuilder();
        builder.append("{\n  Winding: ")
               .append(windingRule)
               .append('\n');
        for (SubPath subPath : subPaths)
            builder.append(subPath);
        builder.append("}\n");
        return builder.toString();
    }

    Point2D.Float currentPoint = null;
    SubPath currentSubPath = null;
    int windingRule = -1;
    final List<SubPath> subPaths = new ArrayList<Path.SubPath>();
}

A ClipPathFinder osztály a következőképpen használatos:

PDDocument document = PDDocument.load(PDFRESOURCE, null);
PDPage page = document.getPage(PAGENUMBER);
ClipPathFinder finder = new ClipPathFinder(page);
finder.findClipPaths();

for (Path path : finder)
{
    System.out.println(path);
}

document.close();
05.02.2015
  • Ez megvalósítható 1.8.8-ban? 05.02.2015
  • Az PDFGraphicsStreamEngine (amelyből a ClipPathFinder származik) a PDFBox 1.8.8 PageDrawer alap egy általánosabb mellékága. Valószínűleg módosítani lehet a PageDrawer class to serve the same purpose as PDFGraphicsStreamEngine. Else one has to do more copy&paste and create one's own graphics stream engine based on the 1.8.8 PDFStreamEngine. To cut a long story short: It is possible but you have to find/create a replacement for the PDFGraphicsStreamEngine alaposztályát. 05.02.2015
  • Még egy kicsit megnézve a kódot... Azt hiszem, megpróbálnám visszaportolni a PDFGraphicsStreamEngine-t és az összes GraphicsOperatorProcessor osztályát, ha szükségem lenne a funkcionalitásra az 1.8.8-ban. 05.02.2015
  • Kíváncsi vagyok, nem lenne-e egyszerűbb beállítani az artbox-ot (vagy más határt) a phantomjs-ban, amit aztán vissza lehet kérni... 05.02.2015
  • vigyor, igen, ezeknek a konstrukcióknak a használata valóban a legjobb az utófeldolgozáshoz. 05.02.2015
  • @mkl, csodálatos válasz. A PDF specifikációiból megértettem, hogy az az útvonal, amelyen a végső festési művelet előtt van egy vágási művelet, meg lesz festve, kivéve, ha az elérési út egy n operátorral végződik. Ez helyes? 20.06.2016
  • Ezenkívül úgy tűnt, hogy a kódja használható a vonalak és görbék megtekintéséhez egy PDF-oldalon (csak annak ellenőrzésére, hogy hány van ott, nem a határolókeretet vagy más információkat, amelyeket gondolom később hozzáadhat), csak hozzáadja a paths.add(currentPath);-t az elérési úthoz. festési módszereket és segments.add(new Line(currentPoint,start)); a SubPath.closePath metódusra. Kipróbáltam ezt az Adobe PDF specifikációval (adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/), 6. oldal, és működött. 21.06.2016
  • Azonban észrevettem valami furcsát az 5. oldalon ebben a PDF-ben (drive.google .com/file/d/0B65bQnJhC1mvbEVQQ0o0QU9STlU/). A fent említett módosított kóddal 956 sort és 48 görbét kaptam. Írtam egy kis kódot is ( pastebin.com/ehtPXgY2 ), hogy megszámoljam a vonalak és görbék számát a PDFStreamParser és ez 1084 sort, 53 görbét és 25 téglalapot (azaz összesen 1184 sort) adott nekem. Szóval egy kicsit tanácstalan vagyok, hogy mi történik. 21.06.2016
  • @rivu Kérjük, ossza meg módosított Path és ClipPathFinder osztályait is az eredmény reprodukálásához. És kérlek, tedd ezt is önálló kérdéssé, nehéz a kódot csak megjegyzésekben megfelelően megvitatni. 21.06.2016
  • @mkl, hozzáadtam egy új kérdést. Nagyon köszönöm, hogy visszatértél. stackoverflow.com/questions/37950491/ 21.06.2016
  • @mkl, bocs, új kérdésem lenne :| itt: stackoverflow.com /questions/38005345/. Megnéznéd? Elnézést, hogy sokszor zavarlak. 24.06.2016
  • Új anyagok

    A rádiógomb ellenőrzött eseményének használata a jQueryben
    Ebben a cikkben látni fogjuk, hogyan kell dolgozni a jquery választógombbal ellenőrzött eseményeivel. A választógombok HTML gombok, amelyek segítenek kiválasztani egyetlen értéket egy csoportból...

    Körkörös függőségek megoldása terraformban adatforrásokkal – lépésről lépésre
    Mi az a körkörös függőségek Dolgozzunk egy egyszerű eseten, amikor az SQS-sor és az S3-vödör közötti körkörös függőség problémája van egy egymástól függő címkeérték miatt. provider..

    Miért érdemes elkezdeni a kódolást 2023-ban?
    01100011 01101111 01100100 01100101 — beep boop beep boop Világunk folyamatosan fejlődik a technológia körül, és naponta fejlesztenek új technológiákat a valós problémák megoldására. Amint..

    🎙 Random Noise #2  – Örökbefogadás és hit
    az analitika íratlan világának gondozása Szeretné, hogy ezek a frissítések a postaládájába kerüljenek? Iratkozzon fel itt . "Ha önvezető autókat gyártanak, akkor mi miért ne..

    A legrosszabb politika és prediktív modellek májátültetésre jelöltek számára az Egyesült Államokban
    A máj (vagy óangolul lifer) az emberi test legnehezebb belső szervére utal, amely csendesen működik a nap 24 órájában. Mit csinál a máj? 500 feladatot hajt végre a szervezet egészségének..

    5 webhely, amely 2022-ben fejleszti front-end fejlesztői készségeit
    Frontendmentor.io A tényleges projektek létrehozásával a Frontendmentor.io segítséget nyújt a front-end kódolási képességeinek fejlesztésében. A kódolást azután kezdheti meg, hogy..

    Mikor kell használni a Type-t az interfészhez képest a TypeScriptben?
    A TypeScript a JavaScript gépelt szuperkészlete, amely statikus gépelést ad a nyelvhez. Ez megkönnyíti a robusztus és karbantartható kód írását azáltal, hogy a hibákat a fordítási időben..